pow() convertido a entero, resultado inesperado

5 minutos de lectura

pow convertido a entero resultado inesperado
Jori

Tengo algunos problemas al usar una conversión de enteros para el pow() función en el lenguaje de programación C. El compilador que estoy usando es el Compilador minúsculo de C (tcc versión 0.9.24) para la plataforma Windows. Al ejecutar el siguiente código, genera el resultado inesperado 100, 99:

#include <stdio.h>
#include <math.h>

int main(void)
{
    printf("%d, ", (int) pow(10, 2));
    printf("%d", (int) pow(10, 2));
    return 0;
}

Sin embargo, en esta compilador en línea, la salida es la esperada: 100, 100. No sé qué está causando este comportamiento. ¿Alguna idea? ¿Error de programación mío, error del compilador?

  • posible duplicado de ¿Qué es un ejemplo simple de punto flotante/error de redondeo?

    usuario529758

    24 de agosto de 2013 a las 10:58

  • Chicos, ¿por qué el primer valor es correcto? ¿No debería ser 99, 99 si fuera el caso del problema habitual de impreciso y luego truncado?

    – ppeterka

    24 de agosto de 2013 a las 11:02


  • @ppeterka Teoría de la conspiración #183742: printf("%d", some_integer) se pliega constantemente en tiempo de compilación. Si el algoritmo de plegado constante en el compilador es defectuoso, es muy posible que el código se cambie a puts("99");. los pow() Sin embargo, la implementación parece ser honesta y correcta (en el sentido de que presta atención a las potencias enteras). pero lo haríamos De Verdad necesita el ensamblaje que generó el compilador para probar esto.

    usuario529758

    24 de agosto de 2013 a las 11:05


  • @H2CO3 mismo resultado (100, 99) con tcc con printf("%d, %d", (int) pow(10, 2), (int) pow(10, 2));. Esto no responde por qué los resultados de las dos llamadas se tratan de manera diferente en tcc.

    – ouah

    24 de agosto de 2013 a las 11:09

  • @H2CO3: No, no es un error de programación de su parte. Podría decirse que es un error de programación en su biblioteca estándar.

    – tmyklebu

    24 de agosto de 2013 a las 21:52

1647672905 73 pow convertido a entero resultado inesperado
masud

Algunas investigaciones en código ensamblador. (OllyDbg)

#include <stdio.h>
#include <math.h>

int main(void)
{
    int x1 = (int) pow(10, 2);
    int x2 = (int) pow(10, 2);
    printf("%d %d", x1, x2);
    return 0;
}

La sección de montaje relacionada:

FLD QWORD PTR DS:[402000]   // Loads 2.0 onto stack
SUB ESP,8
FSTP QWORD PTR SS:[ESP]
FLD QWORD PTR DS:[402008]   // Loads 10.0 onto stack
SUB ESP,8
FSTP QWORD PTR SS:[ESP]
CALL <JMP.&msvcrt.pow>      // Calls pow API
                            // Returned value 100.00000000000000000
...


FLDCW WORD PTR DS:[402042]  //   OH! LOOK AT HERE
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
...

FLD QWORD PTR DS:[402010]   // Loads 2.0 onto stack
SUB ESP,8
FSTP QWORD PTR SS:[ESP]
FLD QWORD PTR DS:[402018]   // Loads 10.0 onto stack
SUB ESP,8
FSTP QWORD PTR SS:[ESP]
CALL <JMP.&msvcrt.pow>      // Calls pow API again
                            // Returned value 99.999999999999999990

El código generado para dos llamadas es el mismo, pero los resultados son diferentes. no se porque tcc puso FLDCW allí. Pero la razón principal de dos valores diferentes es esa línea.

Antes de esa linea la ronda Brocas de control de precisión Mantissa es de 53 bits (10), pero después de la ejecución de esa línea (carga el control de registro FPU) se establecerá en 64 bits (11). Por otro lado Control de redondeo es más cercano entonces 99.999999999999999990 es el resultado. Leer más…

ingrese la descripción de la imagen aquí


Solución:

Después de usar (int) lanzar un float a una intdebe esperar este error numérico, porque esta conversión trunca los valores entre [0, 1) to zero.

Assume the 102 is 99.9999999999. After that cast, the result is 99.

Try to round the result before casting it to integer, for example:

printf("%d", (int) (floor(pow(10, 2) + 0.5)) );

 

  • How are you inspecting the values 100.000… and 99.999…90? Printing floats is non-trivial, and that could possibly be stateful/buggy too.

    – Quadrescence

    Aug 24, 2013 at 20:23

  • @Quadrescence: There is a panel in OllyDbg (Registers FPU) which inspects the values. (I added an image)

    – masoud

    Aug 24, 2013 at 21:05

  • Yes, but when I round, how can I be sure that it isn’t floor‘d to 99.9999 and is still truncated to 99 by the integer conversion? As we just saw that 100 is represented by 99.9999.

    – Jori

    Aug 25, 2013 at 10:28


  • It’s the reason we add 0.5 before floor as written in the above code.

    – masoud

    Aug 25, 2013 at 12:03

  • @Jori: It is clear that in floor(pow(10, 2)), you would want the result to be 100. However, floor is not passed pow(10, 2). It receives only a number, and it cannot determine intent from this number. When floor receives 99.9999…, it must return 99; there is no other correct choice. The actual problem here is that pow is failing; the exact mathematical result of pow(10, 2) is representable; it is 100; but pow is returning a different value. It is pow that is broken (or the C implementation generally), not floor.

    – Eric Postpischil

    Aug 26, 2013 at 13:07


You found a bug in tcc. Thanks for that. The patch has just been commited to the repository. It will be included in the next release, but that might take a while. You can of course pull the source and build it yourself. The patch is here

http://repo.or.cz/w/tinycc.git/commitdiff/73faaea227a53e365dd75f1dba7a5071c7b5e541

pow convertido a entero resultado inesperado
Mysoft

It seems that the rounding method may change, thus requiring an ASM instruction finit to reset the FPU. In FreeBASIC on Windows, I get 99.9999 even in the first try, and so I think for you after the first try it would be a consistent 99.9999. (But I call this undefined behavior indeed, more than a bug in the C runtime’s pow().)

So my advice is not do the conversion with round down. To avoid such issues, use, for example:


int x1 = (int)(pow(10, 2)+.5);

¿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