I have some problems using an integer cast for the pow()
function in the C programming language. The compiler I'm using is the Tiny C Compiler (tcc version 0.9.24) for the Windows platform. When executing the following code, it outputs the unexpected result 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;
}
However, at this online compiler the output is as expected: 100, 100
. I don't know what is causing this behavior. Any thoughts? Programming error from me, compiler bug?
Some investigation in assembly code. (OllyDbg)
The related assembly section:
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 afloat
to anint
, 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:
It seems that the rounding method may change, thus requiring an ASM instruction
finit
to reset the FPU. In FreeBASIC on Windows, I get99.9999
even in the first try, and so I think for you after the first try it would be a consistent99.9999
. (But I call this undefined behavior indeed, more than a bug in the C runtime'spow()
.)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);
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