Some investigation in assembly code. (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;
}
The related assembly section:
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
The generated code for two calls is the same, but the outputs are different. I don't know why tcc put FLDCW
there. But the main reason of two different values are that line.
Before that line the round Mantissa Precision Control Bits is 53bit (10), but after execution of that line (it loads FPU register control) it will set to 64bits (11). On the other hand Rounding Control is nearest so 99.999999999999999990 is the result. Read more...
Solution:
After using (int)
to cast a float
to an int
, you should expect this numeric error, because this casting truncates the values between [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)) );
99, 99
if it was the case for the usual imprecise-then-truncate issue? – Rabbiprintf("%d", some_integer)
is constant-folded at compile time. If the constant-folding algorithm in the compiler is defectious, then the code may very well be changed toputs("99");
. Thepow()
implementation seems to be honest and correct (in the sense that it pays attention to integer powers), though. But we would really need the assembly the compiler generated to prove this. – Jacky100
,99
) withtcc
withprintf("%d, %d", (int) pow(10, 2), (int) pow(10, 2));
. This does not answer why the two calls results are treated differently intcc
. – Selectivityprintf("%d, %d, ", (int) pow(10, 2), (int) pow(10, 2));
? (added,
to second%d
too) – Rabbitcc test.c -lm
and get100 100
– Emulate