Cách gỡ rối những lối thường gặp trong Java [2] – Thời gian cho một thay đổi.

clipart0278

     Xem xét bài toán sau đây: Nam đi đến cửa hàng phụ tùng xe hơi để mua thức ăn với giá $1.10, nhưng anh ấy chỉ có tờ 2 đô la. Anh ấy sẽ nhận được bao nhiêu tiền thối lại nếu thanh toán bằng tờ 2 đô đó. Và chúng ta sẽ giải bài toán đơn giản này trên Java để xem Nam sẽ được thối lại bao nhiêu tiền. Surprised smile

Dưới đây là đoạn chương trình viết trên Java để giải quyết bài toán trên, bạn thử đoán xem nó in ra kết quả gì?

public class Main
{
    public static void main(String[] args) {
    System.out.println(2.00 - 1.10);
    }
}

Lời giải cho vấn đề bài toán

Kết quả chương trình in ra khi chạy là:

run:
0.8999999999999999
BUILD SUCCESSFUL (total time: 0 seconds)

–  Thật lòng bạn có thể mong đợi chương trình in 0.90, nhưng thực tế lại không như bạn nghĩ. Nó in ra một đáp án không tròn 0.9. Vậy vấn đề là lỗi ở đâu?

–  Vấn đề là số 1.1 không thể được biểu diễn chính xác dưới dạng một double, do đó nó được biểu diễn bởi giá trị double gần nhất. Chương trình trên lấy 2 trừ đi giá trị này. Thật không may, kết quả của phép tính này không phải là giá trị gần nhất với 0.9. Dạng biểu diễn ngắn nhất của giá trị double vừa có được là dãy số dài đã in ra ở trên.

–  Thông thường hơn, vấn đề là không phải tất cả số thập phân được biểu diễn chính xác bằng cách dùng dấu chấm động nhị phân. Nếu bạn sử dụng những phiên bản mơi của Java chúng ta có thể sử dụng tiện ích printf để xác lập độ chính xác của dữ liệu xuất:

public class Main
{
    public static void main(String[] args) {
        System.out.printf("%.2f%n", 2.00 - 1.10);
    }
}

–  Đoạn code trên in ra đúng kết quả là 0.9, nhưng không thể hiện một lời giải chung cho bài toán nền tảng: Nó vẫn dùng số học double vốn là dấu động nhị phân. Số học dấu động cung cấp những phép xấp xỉ tốt trên rất nhiều giá trị khác nhau nhưng thường không đạt được kết quả chính xác. Dấu động nhị phân đặc biệt không phù hợp cho các phép tính tiền tệ vì không thể biểu diễn 0.1 – hoặc bất kỳ lũy thừa âm khác của 10 – một cách chính xác như một số thập phân có chiều dài xác định.

–  Một cách để giả bài toán này là sử dụng một kiểu số nguyên, chẳng hạn như int hoặc long và thực hiện việc tính toán bằng cent. Nếu bạn đi theo lộ trình này, hãy chắc chắn rằng kiểu số nguyên đủ lớn để biểu diễn tất cả giá trị mà bạn sẽ sử dụng trong chương trình. Đối với bài toán này, int thì nhiều.  Sau đây là diện mạo của println nếu chúng ta viết nó lại bằng cách dùng giá trị int để tượng trưng cho các giá trị tiền tệ bằng cent. Phiên bản này là 90 cents, đây là một câu trả lời đúng.

public class Main
{
    public static void main(String[] args) {
        System.out.printf((2.00 - 1.10) + "cents");
    }
}

–  Một cách khác để giải quyết bài toán này là sử dụng BigDecimal ốn thực hiện số học thập phân chính xác. Nó cũng quan với kiểu DCIMAL SQL qua JDBC. có một điệu kiện: luôn sử dụng bộ tạo (constructor) BigDecimal (String), không bao giờ sử dụng BigDecimal (double). Constructor thứ 2 này tạo một instance (thể hiện) có giá trị chính xác của đối số của nó: new BigDecimal (.1) trả về một BigDecimal tượng trương cho 0.10000000000000055511151231257827021181583404541015625. Bằng cách sử dụng chính xác BigDecimal, chương trình sẽ in ra kết quả mong đợi là 0.9.

import java.math.BigDecimal;
// thanhcuong.wordpress.com
public class Main {
    public static void main(String[] args) {
        System.out.println(new BigDecimal("2.00").subtract(new BigDecimal("1.10")));
    }
}

–  Phiên bản này không tốt cho lắm vì Java không cung cấp sự hỗ trợ ngôn ngữ cho BigDecimal. Các phép tính với BigDecimal cũng có thể chậm hơn các phép tính với bất kỳ kiểu nguyên thủy, đây có thể là một vấn đề cho một số chương trình vốn sử dụng rất nhiều phép tính thập phân. Nó không quan trọng đối với hầu hết các chương trình.

Tóm lại, tránh float và double nơi đòi hỏi các câu trả lời chính xác; đối với các phép tính tiền tệ, sử dụng int, long hoặc BigDeciaml. Đối với các nhà thiết kế ngôn ngữ, xem xét cung cấp sự hỗ trợ cho số học thập phân. Một phương pháp là cung cấp sự hỗ trợ giới hạn cho việc quá tải toán tử để những toán tử số học có thể làm việc với các kiểu tham số, chẳng hạn như BigDecimal. Một phương pháp khác là cung cấp một kiểu thập phân nguyên thủy như COBOL và PL/I đã thực hiện.

(theo “95 bài toán và giải pháp gỡ rối Java” của tác giả Nguyễn Nam Thuận)

Advertisements

About thanhcuong1990

Handsome and talent!! ^^
This entry was posted in Java and tagged . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s