2011-06-22 18 views
11

Gần đây, tôi đã gặp một lỗi/tính năng bằng nhiều ngôn ngữ. Tôi có một kiến ​​thức rất cơ bản về cách nó gây ra (và tôi muốn giải thích chi tiết), nhưng khi tôi nghĩ về tất cả các lỗi tôi phải làm trong những năm qua, câu hỏi là làm thế nào tôi có thể xác định "Này, điều này có thể gây ra một lỗi vô lý, tôi nên sử dụng các hàm chính xác tùy ý ", những ngôn ngữ khác có lỗi này (và những người không có, lý do tại sao). Ngoài ra, tại sao 0.1 + 0.7 thực hiện điều này và tức là 0.1 + 0.3 không, có bất kỳ ví dụ nổi tiếng nào khác không?int ((0.1 + 0.7) * 10) = 7 bằng nhiều ngôn ngữ. Làm thế nào để ngăn chặn điều này?

PHP

//the first one actually doesn't make any sense to me, 
//why 7 after typecast if it's represented internally as 8? 
debug_zval_dump((0.1+0.7)*10); //double(8) refcount(1) 
debug_zval_dump((int)((0.1+0.7)*10)); //long(7) refcount(1) 
debug_zval_dump((float)((0.1+0.7)*10)); //double(8) refcount(1) 

Python:

>>> ((0.1+0.7)*10) 
7.9999999999999991 
>>> int((0.1+0.7)*10) 
7 

Javascript:

alert((0.1+0.7)*10); //7.999999999999999 
alert(parseInt((0.7+0.1)*10)); //7 

Ruby:

>> ((0.1+0.7)*10).to_i             
=> 7                  
>>((0.1+0.7)*10)              
=> 7.999999999999999              
+2

nó không phải là một lỗi (và SELECT được không bị hỏng): nhìn lên chính xác và respresentation. được hỏi dưới nhiều hình thức, nhiều lần trên SO –

+0

Đó không phải là "lỗi/tính năng trong một số ngôn ngữ" mà là "lỗi/tính năng trong một số bộ vi xử lý". Đó là giới hạn của độ chính xác gấp đôi – JBernardo

+0

Tôi sẽ không downvote câu hỏi này chỉ vì nó đã được hỏi rất nhiều lần trước đây. Là một đơn vị, đó là một câu hỏi hay. Gắn cờ nó như là một bản sao nếu bạn muốn, Mitch. –

Trả lời

6

Đây không phải là vấn đề về ngôn ngữ. Đó là vấn đề chung với float point arithmetic.

+1

Vâng, nó là một vấn đề ngôn ngữ. Nhà thiết kế ngôn ngữ có thể đã chọn giải thích mặc định phù hợp hơn cho các chữ số. Trong Fortress, ví dụ, '0.7' chỉ là cú pháp cú pháp cho số hợp lý '7/10' (lưu ý: đó là số * *" bảy phần mười ", không phải * tính toán *" bảy chia cho mười "). * Và * cơ sở mặc định là số thập phân, không phải nhị phân. Điều này sửa chữa khá nhiều 100% các vấn đề mà hầu như tất cả mọi người chạy vào. (Lưu ý: rõ ràng, máy tính thực sự không có bộ nhớ vô hạn, vì vậy ngay cả pháo đài sẽ phải cắt ngắn và vòng tại một số điểm. Nhưng không cho các trường hợp như tiền hoặc OP's.) –

+0

@ Jörg: Có 'Decimal' loại, mà có thể được sử dụng ** khi cần **. Tôi không thấy một điểm trong việc làm cho rằng mặc định trong ngôn ngữ mục đích chung. – vartec

3

floating point representation of numbers is not exact.

Trong Python, int cắt ngắn nổi về phía không đến số nguyên gần nhất. (int) bằng PHP, parseInt trong Javascript và to_i trong Ruby cũng làm như vậy.

Đây không phải là lỗi; nó chỉ là cách các chức năng này hoạt động.

Ví dụ, từ the docs cho int Python:

chuyển đổi của các số dấu phảy để nguyên sẽ cắt cụt (đối với zero).

0

PHP sử dụng số dấu phẩy động theo mặc định, bạn cần phải truyền theo cách thủ công thành số nguyên.

Bạn nên biết số học dấu chấm động. Các bài viết khác ở đây cung cấp đủ liên kết về điều đó.

Cá nhân tôi sử dụng vòng/ceil/float tùy thuộc vào những gì tôi mong đợi như trái ngược với int

$a = (int) round((0.7 + 0.1) * 10); 
1

Đây là một vấn đề được biết rằng đã làm với đại diện dấu chấm động, từ đó bạn có thể tìm thêm thông tin ở đây :

http://en.wikipedia.org/wiki/IEEE_754-2008

Các vấn đề cụ thể là 7,9 sẽ được chuyển đổi trực tiếp (trunc) đến 7 trong khi chuyển nó đến một int.Trong Python, bạn có thể giải quyết vấn đề này với:

int(round(((0.1+0.7)*10))) 

... và tương tự bằng các ngôn ngữ khác.

Nhưng có, điều này có thể là một vấn đề trong nhiều trường hợp. Số điểm nổi không đủ đáng tin cậy cho các chương trình tính lương, ví dụ.

Có thể những người khác có thể cung cấp cho bạn các gợi ý khác. Hpe điều này giúp, dù sao đi nữa.

1

Sử dụng các mô-đun decimal:

>>> int((decimal.Decimal('0.1')+decimal.Decimal('0.7'))*10) 
8 
Các vấn đề liên quan