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

Estoy obteniendo este problema aquí. Al usar diferentes valores de coma flotante de f estoy obteniendo resultados diferentes. ¿Por qué está pasando esto?problemas en la comparación de coma flotante

+0

Regla general: nunca comparas los números de coma flotante de manera 'exacta'. Simplemente no tiene sentido. Siempre use algunos 'épsilon' – valdo

+4

@valdo: Por lo general, es un mal consejo sin un análisis mucho más completo. –

+0

'void main()' es incorrecto. 'int main (void)' es correcto. –

Respuesta

3

Es porque los valores de punto flotante no son representaciones exactas del número. Todos los números base diez deben estar representados en la computadora como números base 2. Es en esta conversión que se pierde la precisión.

Lea más sobre esto en http://en.wikipedia.org/wiki/Floating_point


Un ejemplo (de encontrarse con este problema en mis días de VB6)

Convertir el número 1.1 a una precisión simple número en coma flotante que necesitamos convertirlo a binario. Hay 32 bits que deben crearse.

Bit 1 es el bit de signo (es negativo [1] o de posición [0]) Bits 2-9 son para el valor del exponente Bits 10-32 son para la mantisa (aka significand, básicamente el coeficiente de Entonces, para 1.1, el valor del punto flotante simple se almacena de la siguiente manera (este es el valor truncado, el compilador puede redondear el bit menos significativo detrás de las escenas, pero lo único que hago es truncarlo, lo cual es un poco menos preciso). pero no cambia los resultados de este ejemplo):

s --exp--- -------mantissa-------- 
0 01111111 00011001100110011001100 

Si no hielo en la mantisa existe el patrón de repetición 0011. 1/10 en binario es como 1/3 en decimal. Continúa para siempre Entonces, para recuperar los valores del valor de coma flotante de precisión simple de 32 bits, primero debemos convertir el exponente y la mantisa en números decimales para poder usarlos.

signo = 0 = un número positivo

exponente: 01111111 = 127

mantisa: 00011001100110011001100 = 838860

Con la mantisa tenemos que convertirlo en un valor decimal. La razón es que hay un entero implícito por delante del número binario (es decir, 1.00011001100110011001100). El número implícito se debe a que la mantisa representa un valor normalizado que se utilizará en la notación científica: 1.0001100110011 .... * 2^(x-127).

Para obtener el valor decimal de 838860 simplemente dividimos por 2^-23 ya que hay 23 bits en la mantisa. Esto nos da 0.099999904632568359375. Agregue el implícito 1 a la mantisa nos da 1.099999904632568359375. El exponente es 127 pero la fórmula requiere 2^(x-127).

Así que aquí es la matemática:

(1 + 099999904632568359375) * 2^(127-127)

1,099999904632568359375 * 1 = 1,099999904632568359375

Como se puede ver 1.1 no es realmente almacena en el valor del punto flotante simple como 1.1.

21

f usa la precisión float, pero 0.98 está en la precisión double de forma predeterminada, por lo que la instrucción f <= 0.98 se compara con la precisión double.

Por lo tanto, el f se convierte en double en la comparación, pero puede hacer que el resultado sea ligeramente mayor que 0,98.

Uso

if(f <= 0.98f) 

o utilizar un double para f lugar.


En detalle ... suponiendo float es IEEE single-precision y double es IEEE double-precision.

Este tipo de números de coma flotante se almacenan con una representación de base 2. En base 2 este número necesita una precisión infinita para representar ya que es un decimal repetido:

0.98 = 0.1111101011100001010001111010111000010100011110101110000101000... 

A float sólo puede almacenar 24 bits de cifras significativas, es decir,

 0.111110101110000101000111_101... 
           ^round off here 
    = 0.111110101110000101001000 

    = 16441672/2^24 

    = 0.98000001907... 

A double puede almacenar 53 bits de cifras signficant, por lo

 0.11111010111000010100011110101110000101000111101011100_00101000... 
                  ^round off here 
    = 0.11111010111000010100011110101110000101000111101011100 

    = 8827055269646172/2^53 

    = 0.97999999999999998224... 

Así que el 0,98 llegarán a ser ligeramente más grande en float y de menor double.

+1

+1 Buen punto, puede estar más cerca de lo que está pidiendo que mi respuesta. – egrunin

+0

¡Me ganaste! +1 – slezica

Cuestiones relacionadas