2011-09-10 29 views
5

tôi đã viết C++ mã sau:Output bất ngờ khi thêm hai số float

float a, b; 
int c; 

a = 8.6; 
b = 1.4; 
c = a + b; 

printf("%d\n", c); 

Đầu ra là 10.

Nhưng khi tôi chạy đoạn mã sau:

float a, b; 
int c; 

a = 8.7; 
b = 1.3; 
c = a + b; 

printf("%d\n", c); 

Đầu ra là 9.

Sự khác biệt giữa hai yếu tố này là gì khi chúng đưa ra các kết quả đầu ra khác nhau?

Trả lời

15

Không có số nào như 8.7 hoặc 1.3 trong dấu phẩy động. Có một số 10, và một số -6,5, và một số 0,96044921875 ... nhưng không có 8,7 hoặc 1,3.

Tốt nhất, máy tính của bạn có thể làm tròn 8.7 đến số dấu phẩy động gần nhất, và làm tròn 1.3 đến số dấu phẩy động gần nhất. Máy tính thêm các số được làm tròn này vào nhau và sau đó làm tròn kết quả.

Không sử dụng số dấu phẩy động để kiếm tiền.

#include <stdio.h> 
int main(int argc, char *argv[]) 
{ 
    float a = 8.7, b = 1.3; 
    printf("Looks like: %.1f + %.1f = %.1f\n", a, b, a+b); 
    printf("The truth: %.20f + %.20f = %.20f\n", a, b, a+b); 
    return 0; 
} 

Trên x86 GCC/máy tính Linux, tôi nhận được kết quả:

 
Looks like: 8.7 + 1.3 = 10.0 
The truth: 8.69999980926513671875 + 1.29999995231628417969 = 9.99999976158142089844 

Trên một máy tính PPC GCC/OS X, tôi nhận được kết quả:

 
Looks like: 8.7 + 1.3 = 10.0 
The truth: 8.69999980926513671875 + 1.29999995231628417969 = 10.00000000000000000000 

Thông báo cách 8.7 và 1.3 được làm tròn xuống trong trường hợp cụ thể này. Nếu bạn chọn số được làm tròn lên, bạn có thể thấy một số lớn hơn 10 ở phía bên tay phải.

Xem Điều mà mỗi nhà khoa học máy tính nên biết về số học dấu chấm động, bởi David Goldberg (link).

+2

Không sử dụng ** binary ** floating-point cho tiền. Dấu phẩy thập phân được chuẩn hóa chính xác cho các ứng dụng tài chính. –

+0

@Pascal Cuoq: Không nên sử dụng số thập phân cố định cho tiền? Ở một số nơi, số học như vậy được yêu cầu về mặt pháp lý ... –

2

Số dấu chấm động không giống như số thực và hành vi của chúng hoàn toàn khác.

Số thực là vô hạn, trong khi số dấu phẩy động là hữu hạn và chỉ có thể đại diện cho một tập con nhỏ của tất cả các số thực có thể.

Vì không phải tất cả các số thực có thể được biểu diễn dưới dạng dấu phẩy động, phép gán điểm hoặc vận hành có thể cung cấp cho bạn kết quả hơi khác so với thực hiện tương tự trong không gian số thực.

Xem wikipedia entry on floating point để được giới thiệu. Phần về floating point accuracy đặc biệt thú vị và đưa ra các ví dụ khác tương tự như của bạn.

0

Không có sự khác biệt thực sự nào giữa hai loại. Cả hai đều hành xử theo những cách không thể đoán trước.

Những gì bạn đang làm tương đương với việc lật đồng xu hai lần và hỏi bạn đã làm gì khác nhau để có được đầu một lần và đuôi người kia. Nó không phải là bạn đã làm bất cứ điều gì khác nhau, đó là điều này xảy ra khi bạn lật tiền xu.

Nếu bạn yêu cầu một người thêm một phần ba và hai phần ba bằng cách sử dụng 6 chữ số thập phân chính xác và sau đó làm tròn xuống số nguyên, bạn có thể nhận được 0 và bạn có thể nhận được 1. Nó sẽ phụ thuộc vào những thứ như chúng đại diện cho 2/3 là "0.666666" hoặc "0.6666667" và cả hai đều chấp nhận được. Vì vậy, cả 0 và 1 đều là câu trả lời chấp nhận được. Nếu bạn không sẵn sàng chấp nhận câu trả lời, đừng hỏi loại câu hỏi đó.

+2

Trong điểm nổi, 0,666667 là đại diện được chấp nhận duy nhất của 2/3 với 6 chữ số thập phân. Các hoạt động nhất định (+, -, *, /, sqrt) được yêu cầu nằm trong phạm vi 1/2 ULPS của câu trả lời chính xác và 0.666666 không đáp ứng tiêu chí này để tính giá trị 2/3. –

+0

Tôi đã không nói về điểm nổi, tôi đã nói về độ chính xác thập phân 6 chữ số, đó là một loại điểm cố định. (Đó là một sự tương tự.) –

+0

Tôi cũng nói về số thập phân 6 chữ số. 1/2 ULP có nghĩa là kết quả của việc tính toán 2/3 phải nằm trong khoảng từ 1/2 x 10^-6 của 2/3, điều này được thỏa mãn bởi các phương pháp làm tròn thông thường được dạy ở trường. Các trường hợp duy nhất mà bạn sẽ không chắc chắn về kết quả là những người có số 5 ở vị trí cuối cùng, ví dụ: 0,5 có thể làm tròn thành 0 hoặc 1 tùy thuộc vào lựa chọn quy tắc của bạn (làm tròn, tròn xuống, tròn thành 0, tròn từ không, quay về phía ngay cả). Tuy nhiên, không có tập hợp các quy tắc có thể chấp nhận được khiến 2/3 bao giờ trở thành 0.666666. –