2010-10-18 33 views
6
void main() 
{ 
    float f = 0.98; 
    if(f <= 0.98) 
     printf("hi"); 
    else 
     printf("hello"); 
    getch(); 
} 

Tôi nhận được vấn đề này ở đây.Ở bằng cách sử dụng các giá trị điểm nổi khác nhau của f i nhận được kết quả khác nhau. Tại sao điều này xảy ra?vấn đề trong so sánh điểm nổi

+0

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

+4

@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. –

+0

'void main()' là sai. 'int main (void)' là chính xác. –

Trả lời

3

Đó 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.

21

f đang sử dụng float chính xác, nhưng 0.98 có độ chính xác là double, do đó, tuyên bố f <= 0.98 được so sánh với độ chính xác double.

Do đó, f được chuyển đổi thành một số double, nhưng có thể làm cho kết quả hơi lớn hơn 0,98.

Sử dụng

if(f <= 0.98f) 

hoặc sử dụng một double cho f để thay thế.


Cụ thể ... giả floatIEEE single-precisiondoubleIEEE double-precision.

Các loại số dấu phẩy động này được lưu trữ với biểu diễn cơ số 2. Trong cơ sở-2 số này phải được sự chính xác vô hạn để biểu diễn vì nó là một số thập phân lặp đi lặp lại:

0.98 = 0.1111101011100001010001111010111000010100011110101110000101000... 

Một float chỉ có thể lưu trữ 24 bit của các nhân vật quan trọng, tức là

 0.111110101110000101000111_101... 
           ^round off here 
    = 0.111110101110000101001000 

    = 16441672/2^24 

    = 0.98000001907... 

Một double có thể lưu trữ 53 bit của các con số đáng chú ý, do đó,

 0.11111010111000010100011110101110000101000111101011100_00101000... 
                  ^round off here 
    = 0.11111010111000010100011110101110000101000111101011100 

    = 8827055269646172/2^53 

    = 0.97999999999999998224... 

Vì vậy, 0,98 sẽ trở nên lớn hơn một chút trong float và nhỏ hơn trong double.

+1

+1 Điểm tốt, có thể gần hơn với những gì anh ta hỏi hơn câu trả lời của tôi. – egrunin

+0

Bạn đánh bại tôi với nó! +1 – slezica

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