Fundamentos de C: ¿la variable doble no es igual a la expresión doble?

4 minutos de lectura

Estoy trabajando con una matriz de dobles llamada indata (en el montón, asignado con malloc), y un doble local llamado sum.

Escribí dos funciones diferentes para comparar valores en indata, y obtuvo diferentes resultados. Finalmente, determiné que la discrepancia se debía a que una función usaba una expresión en una prueba condicional y la otra función usaba una variable local en la misma prueba condicional. Esperaba que estos fueran equivalentes.

Mi función A usa:

    if (indata[i]+indata[j] > max) hi++;

y mi función B usa:

    sum = indata[i]+indata[j];
    if (sum>max) hi++;

Después de pasar por el mismo conjunto de datos y maxtermino con diferentes valores de hi dependiendo de la función que use. Creo que la función B es correcta y la función A es engañosa. Del mismo modo, cuando pruebo el fragmento a continuación

    sum = indata[i]+indata[j];
    if ((indata[i]+indata[j]) != sum) etc.

ese condicional se evaluará como verdadero.

Si bien entiendo que los números de punto flotante no necesariamente brindan una representación exacta, ¿por qué esa representación inexacta cambia cuando se evalúa como una expresión en lugar de almacenarse en una variable? ¿Es la mejor práctica recomendada evaluar siempre una expresión doble como esta antes de un condicional? ¡Gracias!

  • Básicamente se debe a que las computadoras no pueden representar números con total precisión. Lee sobre el punto flotante.

    – Iharob Al Asimi

    4 de junio de 2016 a las 5:26

  • @iharob Lo reconoció en su último párrafo. Pero no explica por qué es diferente dependiendo de si asigna el resultado a una variable.

    – Barmar

    4 de junio de 2016 a las 5:28

  • Cuando la asignación tiene lugar en B, se requiere redondear el valor al más cercano double (típicamente 64 bits). En la función A, la expresión condicional mayo evaluarse utilizando una mayor precisión (por ejemplo, 80 bits).

    – usuario3386109

    4 de junio de 2016 a las 5:29

  • @user3386109: si el código está realmente compilado para x86 en lugar de x86-64 para que use x87 en lugar de instrucciones de punto flotante SSE, entonces esa es la explicación más probable.

    – Dolda2000

    4 de junio de 2016 a las 5:34


  • Ver explorebinary.com/cuando-los-dobles-no-se-comportan-como-dobles

    –Rick Regan

    04/06/2016 a las 12:51

Fundamentos de C ¿la variable doble no es igual a
R.. GitHub DEJAR DE AYUDAR A ICE

Sospecho que está utilizando x86 de 32 bits, la única arquitectura común sujeta a exceso de precisión. En C, expresiones de tipo float y double en realidad se evalúan como float_t o double_tcuyas relaciones con float y double se reflejan en el FLT_EVAL_METHOD macro. En el caso de x86, ambos se definen como long double porque la fpu no es realmente capaz de realizar operaciones aritméticas con precisión simple o doble. (Tiene bits de modo destinados a permitir eso, pero el comportamiento es ligeramente incorrecto y, por lo tanto, no se puede usar).

Asignar a un objeto de tipo float o double es una forma de forzar el redondeo y deshacerse del exceso de precisión, pero también puede agregar un molde gratuito a (double) si prefiere dejarlo como una expresión sin asignaciones.

Tenga en cuenta que forzar el redondeo a la precisión deseada no es equivalente a realizar la aritmética con la precisión deseada; en lugar de un paso de redondeo (durante la aritmética), ahora tiene dos (durante la aritmética, y nuevamente para eliminar la precisión no deseada), y en los casos en que el primer redondeo le da un punto medio exacto, el segundo redondeo puede ir en el ‘equivocado’. dirección. Este problema generalmente se llama redondeo dobley hace que el exceso de precisión sea significativamente peor que la precisión nominal para ciertos tipos de cálculos.

  • Gracias por la explicación. Estoy ejecutando el código en una CPU i7-3770 con Windows 7 de 64 bits. Sin embargo, mi compilador es minGW, que es una aplicación de 32 bits. Voy a investigar la configuración del compilador. Entiendo que hay un nivel de precisión de hardware superior al doble, y seré más cuidadoso con el uso de expresiones frente a variables. Para tu información, escribir la expresión en realidad no funciona en este caso, el mismo comportamiento que sin ella.

    – aún aprendiendo

    5 de junio de 2016 a las 3:34

  • Prueba el yeso con -std=c99 o -fexcess-precision=standard. En algunos modos que no cumplen con los estándares, GCC se comporta incorrectamente.

    – R.. GitHub DEJA DE AYUDAR A ICE

    6 de junio de 2016 a las 2:55

  • Gracias: cualquiera de esas opciones de línea de comandos del compilador soluciona el problema, de modo que este código se comporta correctamente: if ((double)(indata[i]+indata[j]) > max) hi++;

    – aún aprendiendo

    20 de junio de 2016 a las 21:21


¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad