Đó là vì giá trị dấu phẩy động không đại diện chính xác cho số. Tất cả mười số cơ sở cần phải được trình bày trên máy tính dưới dạng số cơ sở 2. Trong chuyển đổi này, độ chính xác bị mất.
Đọc thêm về vấn đề này tại http://en.wikipedia.org/wiki/Floating_point
Một ví dụ (từ gặp phải vấn đề này trong những ngày VB6 của tôi)
Để chuyển đổi số 1,1 đến một độ chính xác số dấu chấm động đơn chúng ta cần phải chuyển đổi nó thành nhị phân. Có 32 bit cần được tạo.
Bit 1 là bit dấu (là số âm [1] hoặc vị trí [0]) bit 2-9 cho giá trị số mũ bit 10-32 là cho phần định trị (aka significand, về cơ bản là hệ số Vì vậy, đối với 1,1 giá trị dấu phẩy động duy nhất được lưu trữ như sau (đây là giá trị cắt ngắn, trình biên dịch có thể làm tròn chút ít quan trọng nhất phía sau hậu trường, nhưng tất cả những gì tôi làm là cắt bớt nó, điều này hơi kém chính xác hơn một chút. nhưng không thay đổi kết quả của ví dụ này):
s --exp--- -------mantissa--------
0 01111111 00011001100110011001100
Nếu bạn không băng trong mantissa có mẫu lặp lại 0011. 1/10 trong hệ nhị phân giống 1/3 trong thập phân. Nó cứ mãi mãi. Vì vậy, để lấy các giá trị từ giá trị điểm nổi chính xác đơn 32 bit, trước tiên chúng ta phải chuyển đổi số mũ và số thập phân thành số thập phân để chúng ta có thể sử dụng chúng.
dấu = 0 = số dương
mũ: 01111111 = 127
mantissa: 00011001100110011001100 = 838860
Với mantissa chúng ta cần phải chuyển nó sang một giá trị thập phân. Lý do là có một số nguyên ngụ ý trước số nhị phân (ví dụ: 1.00011001100110011001100). Số được ngụ ý là vì phần tử đại diện cho một giá trị chuẩn hóa được sử dụng trong ký pháp khoa học: 1.0001100110011 .... * 2^(x-127).
Để lấy giá trị thập phân ra khỏi 838860, chúng tôi chỉ chia cho 2^-23 vì có 23 bit trong phần định trị. Điều này cho chúng ta 0.099999904632568359375. Thêm hàm ý nghĩa 1 vào phần định trị cho chúng ta 1.099999904632568359375. Số mũ là 127 nhưng công thức gọi 2^(x-127).
Vì vậy, đây là toán:
(1 + 099999904632568359375) * 2^(127-127)
1,099999904632568359375 * 1 = 1,099999904632568359375
Như bạn thấy 1.1 là không thực sự được lưu trữ trong giá trị dấu phẩy động đơn lẻ là 1.1.
Quy tắc chung: không bao giờ bạn so sánh các số dấu phẩy động theo kiểu 'chính xác'. Nó chỉ không có ý nghĩa. Luôn sử dụng một số 'epsilon' – valdo
@valdo: Đó thường là lời khuyên không tốt mà không cần phân tích kỹ lưỡng hơn. –
'void main()' là sai. 'int main (void)' là chính xác. –