2008-11-29 23 views
9

Trong chương trình của tôi, tôi có một mảng với 25 giá trị gấp đôi 0,04 Khi tôi cố gắng tổng hợp các giá trị trong một vòng lặp tôi nhận được kết quả như sau:Strange hành vi dấu chấm động trong một chương trình Java

0.0 + 0.04 = 0.04 
0.04 + 0.04 = 0.08 
0.08 + 0.04 = 0.12 
0.12 + 0.04 = 0.16 
0.16 + 0.04 = 0.2 
0.2 + 0.04 = 0.24000000000000002 
0.24000000000000002 + 0.04 = 0.28 
0.28 + 0.04 = 0.32 
0.32 + 0.04 = 0.36 
0.36 + 0.04 = 0.39999999999999997 
0.39999999999999997 + 0.04 = 0.43999999999999995 
0.43999999999999995 + 0.04 = 0.4799999999999999 
0.4799999999999999 + 0.04 = 0.5199999999999999 
0.5199999999999999 + 0.04 = 0.5599999999999999 
0.5599999999999999 + 0.04 = 0.6 
0.6 + 0.04 = 0.64 
0.64 + 0.04 = 0.68 
0.68 + 0.04 = 0.7200000000000001 
0.7200000000000001 + 0.04 = 0.7600000000000001 
0.7600000000000001 + 0.04 = 0.8000000000000002 
0.8000000000000002 + 0.04 = 0.8400000000000002 
0.8400000000000002 + 0.04 = 0.8800000000000002 
0.8800000000000002 + 0.04 = 0.9200000000000003 
0.9200000000000003 + 0.04 = 0.9600000000000003 

Tại sao trên trái đất điều đó có thể xảy ra không ?!

+0

Câu hỏi liên quan đến một số câu hỏi: http://stackoverflow.com/questions/327020/why-are-floating-point-values-so-prolific – CesarB

Trả lời

23

Bộ nhớ phổ biến nhất cho các giá trị dấu phẩy động trong ngôn ngữ lập trình - IEEE singles and doubles - không có đại diện chính xác cho hầu hết các phân số thập phân.

Lý do là chúng lưu trữ các giá trị ở định dạng dấu phẩy động nhị phân, thay vì định dạng dấu phẩy động thập phân. Các giá trị phân số chỉ có thể được biểu diễn chính xác là những giá trị là tổng của các lũy thừa âm của hai. Số như:

  • 0,5 (2^-1)
  • 0,125 (2^-3)
  • 0,625 (2^-1 + 2^-3)

vv

Những gì bạn thấy là sự biểu diễn các số như 0,96 không chính xác được thể hiện, bởi vì chúng không thể diễn tả như là một tổng của các lũy thừa âm của hai. Do đó, khi được in ra với độ chính xác đầy đủ dưới dạng phần thập phân, chúng sẽ không khớp với giá trị ban đầu.

+0

"Thay vì thập phân" kinda đưa ra ý tưởng sai. Nếu nó được sử dụng thập phân, nó sẽ được giới hạn trong các giá trị phân số là tổng của các quyền hạn âm của mười, do đó sẽ không thực sự giải quyết nhiều. Ai đó đọc câu trả lời của bạn có thể nhận được ý tưởng rằng "vấn đề sẽ biến mất nếu chỉ họ sử dụng base10" – jalf

+0

@ jalf, đúng nhưng ít nhất một số thập phân dấu chấm động sẽ làm ngạc nhiên ít người sử dụng để tính toán túi đơn giản. Thêm một đống 20 nick và không nhận được chính xác một buck là đáng ngạc nhiên cho rất nhiều người dân. Một ký hiệu thập phân sẽ nhận được trường hợp đó đúng, và giao dịch cho các vấn đề buồn cười với những con số nên quen thuộc với các bài học số học cấp lớp. – RBerteig

14
+0

Tài liệu đó rất dày đặc đối với một người mới biểu diễn dấu phẩy động trong hầu hết các ngôn ngữ lập trình.Tôi sẽ không giới thiệu nó bên ngoài mọi người với ít nhất một năm, nếu không phải là ba, trong giáo dục CS. –

+0

Tôi đồng ý. Mặt khác, nó là tốt đẹp cho các học viên tự học và các overachievers để biết nó ở đâu, trong trường hợp họ đang đối mặt với vấn đề số. –

+3

Nó vẫn là một cái gì đó mọi người nên biết, ngay cả khi nó quá dày đặc cho họ để đọc mặc dù. ;) – jalf

5

câu trả lời khác được đề cập tại sao, nhưng không làm thế nào để tránh nó.

Có một số giải pháp:

  • Scaling: nếu tất cả các số của bạn là bội số của 0.01 (ví dụ), nhân tất cả mọi thứ bằng 100 và sử dụng số nguyên số học (đó là chính xác).
  • Loại số: nếu ngôn ngữ của bạn có loại số (như loại numeric trong SQL), bạn có thể sử dụng nó.
  • Các lý do chính xác tùy ý: sử dụng thư viện bignum như GMP, cho phép bạn trình bày các số này dưới dạng tỷ lệ của hai số nguyên.
  • Dấu phẩy thập phân: nếu bạn có dấu phẩy động thập phân như điểm trong IEEE-754r, bạn có thể sử dụng nó.
+0

+1 cho lý do hợp lý – finnw

1

Bạn có thể muốn xem lớp java BigDecimal thay thế cho phao nổi và tăng gấp đôi.

Các vấn đề liên quan