2011-11-09 38 views
7

Tôi tình cờ gặp ví dụ sau trên wikipedia (http://en.wikipedia.org/wiki/Type_conversion#Implicit_type_conversion).Chuyển đổi loại tiềm ẩn trong C

#include <stdio.h> 

int main() 
{ 
    int i_value = 16777217; 
    float f_value = 16777217.0; 
    printf("The integer is: %i\n", i_value); // 16777217 
    printf("The float is: %f\n", f_value); // 16777216.000000 
    printf("Their equality: %i\n", i_value == f_value); // result is 0 
} 

lời giải thích của họ: "Hành vi kỳ quặc này được gây ra bởi một diễn viên tiềm ẩn của i_value nổi khi nó được so sánh với f_value; một dàn diễn viên mà mất độ chính xác, làm cho các giá trị được so sánh khác nhau"

Điều này không đúng? Nếu i_value được cast thành float, thì cả hai sẽ có cùng độ mất chính xác và chúng sẽ bằng nhau. Vì vậy, i_value phải được truyền thành gấp đôi.

+0

Với g + + (GCC 4.6.2) tôi nhận được '1' cho sự bình đẳng. –

+0

@Kerrek: Và tôi. Trong VS, tôi nhận được 0. –

+0

@OliCharlesworth: Tôi tò mò về việc thay đổi chữ thành 'f' hoặc kiểu thành' double' - tôi nhận '1' trong mọi trường hợp ... –

Trả lời

7

Không, trong trường hợp của các nhà điều hành bình đẳng, các "chuyển đổi số học bình thường" xảy ra, mà bắt đầu:

  • Đầu tiên, nếu loại thực tương ứng của một trong hai toán hạng là long double, các toán hạng khác được chuyển đổi, không thay đổi loại tên miền, thành loại có loại thực tương ứng là long double.
  • Nếu không, nếu loại thực tế tương ứng của toán hạng là double, toán hạng khác được chuyển đổi, không thay đổi loại tên miền, thành loại có loại thực tương ứng là double.
  • Nếu không, nếu loại thực tế tương ứng của toán hạng là float, toán hạng khác được chuyển đổi, mà không thay đổi loại tên miền, thành loại có loại thực tương ứng là float.

trường hợp cuối cùng này áp dụng ở đây: i_value được chuyển thành float.

Lý do mà bạn có thể thấy một kết quả lẻ từ so sánh, này, là vì bối cảnh này để chuyển đổi số học thông thường:

Các giá trị của toán hạng nổi và các kết quả của nổi Các biểu thức có thể được thể hiện ở độ chính xác và phạm vi lớn hơn mà loại yêu cầu; các loại không được thay đổi.

This is what is happening: the type of the converted i_value is still float, but in this expression your compiler is taking advantage of this latitude and representing it in greater precision than float. Đây là hành vi trình biên dịch điển hình khi biên dịch cho điểm nổi tương thích 387, bởi vì trình biên dịch để lại các giá trị tạm thời trên ngăn xếp dấu chấm động, lưu trữ các số dấu chấm động trong định dạng chính xác mở rộng 80 bit.

Nếu trình biên dịch của bạn là gcc, bạn có thể tắt tính chính xác bổ sung này bằng cách cung cấp tùy chọn dòng lệnh -ffloat-store.

+0

Trên x64 gcc sử dụng lệnh cvtsi2ssl rõ ràng để chuyển đổi số nguyên thành float. Tuy nhiên, trên x86, đây là những gì chính xác xảy ra và độ chính xác lớn hơn trong thực tế thậm chí còn lớn hơn gấp đôi. –

+0

@ konrad.kruczynski: Có, và bạn có thể nhận được kết quả tương tự trên x86 bằng cách cung cấp tùy chọn '-mfpmath = sse' (yêu cầu cũng đòi hỏi' -msse' hoặc một tùy chọn ngụ ý rằng). – caf

-1

Tôi tin rằng giá trị số nguyên lớn nhất mà điểm nổi IEEE 32 bit có thể giữ là 1048576 nhỏ hơn số ở trên. Vì vậy, chắc chắn là giá trị điểm động sẽ không giữ chính xác 16777217.

Phần tôi không chắc chắn về cách trình biên dịch so sánh giữa hai loại số khác nhau (tức là phao và int) . Tôi có thể nghĩ đến ba cách khác nhau này có thể được thực hiện:

1) Chuyển đổi cả hai giá trị để "nổi" (điều này sẽ làm cho các giá trị như nhau, vì vậy đây có lẽ không phải những gì các trình biên dịch hiện)

2) Chuyển đổi cả hai giá trị thành "int" (điều này có thể hoặc không thể hiển thị chúng giống nhau ... chuyển đổi thành int thường cắt, vì vậy nếu giá trị dấu phẩy động là 16777216.99999, thì chuyển thành "int" sẽ cắt ngắn)

3) Chuyển đổi cả hai giá trị thành "gấp đôi". Tôi đoán rằng đây là những gì trình biên dịch sẽ làm. Nếu đây là những gì trình biên dịch làm, hai giá trị chắc chắn sẽ khác nhau. Một đôi có thể giữ chính xác 16777217, và nó cũng có thể đại diện chính xác giá trị điểm phao mà 16777217.0 chuyển đổi thành (không chính xác là 16777217.0).

+3

1048576 là 2^20, vì vậy điều này là không chính xác. –

0

Có một số câu trả lời hay tại đây. Bạn phải chuyển đổi rất cẩn thận giữa các số nguyên khác nhau và các biểu diễn điểm nổi khác nhau.

Tôi thường không kiểm tra các số dấu phẩy động cho sự bình đẳng, đặc biệt nếu một trong số chúng xuất phát từ một diễn viên tiềm ẩn hoặc rõ ràng từ một loại số nguyên. Tôi làm việc trên một ứng dụng đầy tính toán hình học. Càng nhiều càng tốt, chúng tôi làm việc với các số nguyên chuẩn hóa (bằng cách buộc một độ chính xác tối đa mà chúng tôi sẽ chấp nhận trong dữ liệu đầu vào). Đối với trường hợp bạn phải sử dụng dấu phẩy động, chúng tôi sẽ áp dụng giá trị tuyệt đối cho chênh lệch nếu cần so sánh.

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