2011-06-07 19 views
7

Hoy estaba rastreando por qué mi programa obtenía algunos errores inesperados de suma de comprobación, en algún código que escribí que serializa y deserializa valores de coma flotante IEEE-754, en un formato que incluye un valor de suma de comprobación de 32 bits (que se calcula ejecutando un algoritmo de tipo CRC sobre los bytes de la matriz de coma flotante).¿Cómo el operador C == decide si dos valores de coma flotante son iguales o no?

Después de un poco de rascarse la cabeza, me di cuenta de que el problema era 0.0f y -0.0f tienen patrones de bits diferentes (0x00000000 vs 0x00000080 (little-endian), respectivamente), pero se consideran equivalentes por C++ igualdad-operador. Entonces, los errores de suma de comprobación no ocurrieron porque mi algoritmo de suma de comprobación recogió la diferencia entre esos dos patrones de bits, mientras que otras partes de mi base de código (que usan pruebas de igualdad de coma flotante, en lugar de mirar los valores byte por by byte) no hizo esa distinción.

De acuerdo, es justo: probablemente debería haber sabido que no debía hacer pruebas de igualdad de punto flotante de todos modos.

Pero esto me hizo pensar, ¿hay otros valores de punto flotante IEEE-754 que se consideran iguales (según el operador C ==) pero tienen diferentes patrones de bits? O, para decirlo de otra manera, ¿cómo exactamente el operador == decide si dos valores de coma flotante son iguales? Newbie me creo que estaba haciendo algo parecido a memcmp() en sus patrones de bits, pero claramente es más matizado que eso.

Aquí hay un ejemplo de código de lo que quiero decir, en caso de que no fuera claro anteriormente.

#include <stdio.h> 

static void PrintFloatBytes(const char * title, float f) 
{ 
    printf("Byte-representation of [%s] is: ", title); 
    const unsigned char * p = (const unsigned char *) &f; 
    for (int i=0; i<sizeof(f); i++) printf("%02x ", p[i]); 
    printf("\n"); 
} 

int main(int argc, char ** argv) 
{ 
    const float pzero = -0.0f; 
    const float nzero = +0.0f; 
    PrintFloatBytes("pzero", pzero); 
    PrintFloatBytes("nzero", nzero); 
    printf("Is pzero equal to nzero? %s\n", (pzero==nzero)?"Yes":"No"); 
    return 0; 
} 
+0

http://how-to.wikia.com/wiki/Howto_compare_floating_point_numbers_in_the_C_programming_language su información, utilizando un épsilon es el camino a seguir para las comparaciones de flotación. No sobre el tema, pero útil para saber. – darvids0n

+1

NaNs podría ir en cualquier dirección (posiblemente según el compilador). También pueden ser diferentes en memoria ya que hay una gran cantidad de NaN posibles (2^24-1 para precisión simple). – ughoavgfhw

Respuesta

13

Utiliza las reglas de igualdad IEEE-754.

  • -0 == +0
  • NaN != NaN
+2

+1 NaN. Tenga en cuenta que este es un caso donde los patrones de bits serían el * mismo *, pero '==' devolvería 'falso'. –

+1

+1 'NaN'. También tenga en cuenta que puede tener diferentes patrones de bits que representan los dos 'NaN's. – trutheality

+0

De hecho, +0.0 y -0.0 son los únicos dos patrones de bits distintos que se pueden comparar iguales. –

1

comparación exacta. Es por eso que es mejor evitar == como una prueba en flotadores. Puede conducir a errores inesperados y sutiles.

Un ejemplo típico es el código:

float f = 0.1f; 

if((f*f) == 0.01f) 
    printf("0.1 squared is 0.01\n"); 
else 
    printf("Surprise!\n"); 

debido 0.1 no se puede representar exactamente en binario (es un lo que sea que se llama a la repetición de un binario fraccional) 0.1*0.1 no será exactamente 0.01 - y, por lo tanto, la prueba de igualdad no funcionará.

analistas numéricos se preocupan por esto en detalle, pero para una primera aproximación es útil para definir un valor APL - llamó pelusa - que es cómo de cerca dos flotadores necesitan llegar a ser considerados iguales. Así que es posible, por ejemplo, #define FUZZ 0.00001f y la prueba

float f = 0.1f; 

if(abs((f*f)-0.01f) < FUZZ) 
    printf("0.1 squared is 0.01\n"); 
else 
    printf("Surprise!\n"); 
+6

Claramente, no es una comparación exacta en el nivel de bit por bit, o no indicaría eso (-0.0f == 0.0f) porque esos dos valores tienen diferentes patrones de bits. –

+0

Un punto razonable que sería más apropiado si escribiera "comparación exacta bit a bit". –

2

Para las plataformas de Windows, this link has:

  • Dividir por 0 produce +/- INF, excepto 0/0 que resulta en NaN.
  • log de (+/-) 0 produce -INF. log de un valor negativo (distinto de -0) produce NaN.
  • La raíz cuadrada recíproca (rsq) o la raíz cuadrada (sqrt) de un número negativo produce NaN. La excepción es -0; sqrt (-0) produce -0, y rsq (-0) produce -INF.
  • INF - INF = NaN
  • (+/-) INF/(+/-) INF = NaN
  • (+/-) INF * 0 = NaN
  • NaN (cualquier OP) cualquier valor = NaN
  • Las comparaciones EQ, GT, GE, LT y LE, cuando uno o ambos operandos son NaN devuelve FALSE.
  • Las comparaciones ignoran el signo de 0 (por lo que +0 es igual a -0).
  • La comparación NE, cuando uno o ambos operandos es NaN devuelve TRUE.
  • Las comparaciones de cualquier valor que no sea NaN contra +/- INF devuelven el resultado correcto.
Cuestiones relacionadas