ví dụ: với đồng đô la, bạn không bao giờ có độ chính xác dưới 0,01 đô la
Ồ vậy sao
vấn đề cũ về lý do tại sao bạn không nên lưu trữ tiền tệ dưới dạng số dấu phẩy động IEEE 754.
Xin vui lòng lưu trữ inch trong số dấu phẩy động IEEE 754 . Họ lưu trữ chính xác như bạn mong đợi.
Xin vui lòng lưu trữ bất kỳ số tiền nào trong các số dấu phẩy động IEEE 754 mà bạn có thể lưu trữ bằng cách sử dụng các dấu chia chia thước thành các phân số của một inch.
Tại sao? Bởi vì khi bạn sử dụng IEEE 754 , đó là cách bạn lưu trữ nó.
Điều về inch là chúng được chia thành hai nửa. Điều về hầu hết các loại tiền tệ là chúng được chia thành một phần mười (một số loại không nhưng hãy tập trung).
Sự khác biệt này sẽ không gây nhầm lẫn ngoại trừ, đối với hầu hết các ngôn ngữ lập trình, đầu vào và đầu ra từ các số dấu phẩy động của IEEE 754 được biểu thị bằng số thập phân! Điều này rất lạ bởi vì chúng không được lưu trữ trong số thập phân.
Bởi vì điều này bạn không bao giờ có thể thấy các bit làm những điều kỳ lạ khi bạn yêu cầu máy tính lưu trữ 0.1
. Bạn chỉ thấy sự kỳ lạ khi bạn làm toán chống lại nó và nó có những lỗi lạ.
Từ java hiệu quả của Josh Bloch :
System.out.println(1.03 - .42);
Sản xuất 0.6100000000000001
Điều gì nói nhiều nhất về điều này không phải là 1
cách ngồi phía bên phải. Đó là những con số kỳ lạ phải được sử dụng để có được nó. Thay vì sử dụng ví dụ phổ biến nhất 0.1
, chúng ta phải sử dụng một ví dụ cho thấy vấn đề và tránh làm tròn sẽ che giấu nó.
Ví dụ, tại sao điều này làm việc?
System.out.println(.01 - .02);
Sản xuất -0.01
Bởi vì chúng tôi đã gặp may mắn.
Tôi ghét những vấn đề khó chẩn đoán vì đôi khi tôi gặp "may mắn".
IEEE 754 đơn giản là không thể lưu trữ chính xác 0,1. Nhưng nếu bạn yêu cầu nó lưu 0,1 và sau đó yêu cầu nó in thì nó sẽ hiển thị 0,1 và bạn sẽ nghĩ mọi thứ đều ổn. Điều đó không ổn, nhưng bạn không thể thấy điều đó bởi vì nó làm tròn để trở về 0,1.
Một số người nhầm lẫn giữa những người khác bằng cách gọi những sai lệch làm tròn số sai lệch này. Không, đây không phải là lỗi làm tròn. Việc làm tròn đang thực hiện những gì nó được yêu cầu và biến những gì không phải là số thập phân thành số thập phân để nó có thể in trên màn hình.
Nhưng điều này che giấu sự không phù hợp giữa cách hiển thị số và cách lưu trữ. Lỗi không xảy ra khi làm tròn số xảy ra. Nó xảy ra khi bạn quyết định đặt một số vào một hệ thống không thể lưu trữ chính xác và cho rằng nó được lưu trữ chính xác khi không.
Không ai mong muốn π sẽ lưu trữ chính xác trong một máy tính và họ quản lý để làm việc với nó tốt. Vì vậy, vấn đề thậm chí không phải là về độ chính xác. Đó là về độ chính xác dự kiến. Máy tính hiển thị một phần mười 0.1
giống như máy tính của chúng tôi, vì vậy chúng tôi hy vọng chúng sẽ lưu trữ một phần mười hoàn hảo như cách máy tính của chúng tôi làm. Họ không. Đó là điều đáng ngạc nhiên, vì máy tính đắt hơn.
Hãy để tôi chỉ cho bạn sự không phù hợp:
Lưu ý rằng 1/2 và 0,5 xếp hàng hoàn hảo. Nhưng 0,1 không xếp hàng. Chắc chắn bạn có thể tiến gần hơn nếu bạn tiếp tục chia cho 2 nhưng bạn sẽ không bao giờ đánh chính xác. Và chúng ta cần nhiều bit hơn mỗi lần chúng ta chia cho 2. Vì vậy, đại diện 0,1 với bất kỳ hệ thống nào chia cho 2 cần một số lượng bit vô hạn. Ổ cứng của tôi không lớn như vậy.
Vì vậy, IEEE 754 ngừng cố gắng khi hết bit. Thật tuyệt vì tôi cần chỗ trên ổ cứng để ... ảnh gia đình. Không thực sự. Nhưng bưc ảnh gia đinh. : P
Dù sao, những gì bạn gõ và những gì bạn nhìn thấy là số thập phân (bên phải) nhưng những gì bạn lưu trữ là nhị phân (bên trái). Đôi khi những điều đó là hoàn toàn giống nhau. Đôi khi họ không. Đôi khi, nó giống như khi chúng không giống nhau. Đó là làm tròn.
Đặc biệt, những gì chúng ta cần biết để có thể lưu trữ giá trị bằng một số loại tiền và in ra?
Xin vui lòng, nếu bạn đang xử lý tiền dựa trên số thập phân của tôi, đừng sử dụng phao hoặc nhân đôi.
Nếu bạn chắc chắn những thứ như một phần mười đồng xu sẽ không được tham gia thì chỉ cần lưu trữ đồng xu. Nếu bạn không thì hãy tìm ra đơn vị nhỏ nhất của loại tiền này sẽ là gì và sử dụng nó. Nếu bạn không thể, hãy sử dụng một cái gì đó như BigDecimal .
Giá trị ròng của tôi có thể sẽ luôn phù hợp với số nguyên 64 bit, nhưng những thứ như BigInteger hoạt động tốt cho các dự án lớn hơn thế. Chúng chỉ chậm hơn các loại bản địa.
Tìm hiểu làm thế nào để lưu trữ nó chỉ là một nửa vấn đề. Hãy nhớ rằng bạn cũng phải có thể hiển thị nó. Một thiết kế tốt sẽ tách hai điều này. Vấn đề thực sự với việc sử dụng phao ở đây là hai thứ đó được kết hợp với nhau.