I have seen various answer here that depicts Strange behavior of pow
function in C.
But I Have something different to ask here.
In the below code I have initialized int x = pow(10,2)
and int y = pow(10,n)
(int n = 2)
.
In first case it when I print the result it shows 100
and in the other case it comes out to be 99
.
I know that pow
returns double
and it gets truncated on storing in int
, but I want to ask why the output comes to be different.
CODE1
#include<stdio.h>
#include<math.h>
int main()
{
int n = 2;
int x;
int y;
x = pow(10,2); //Printing Gives Output 100
y = pow(10,n); //Printing Gives Output 99
printf("%d %d" , x , y);
}
Output : 100 99
Why is the output coming out to be different. ?
My gcc version is 4.9.2
Update :
Code 2
int main()
{
int n = 2;
int x;
int y;
x = pow(10,2); //Printing Gives Output 100
y = pow(10,n); //Printing Gives Output 99
double k = pow(10,2);
double l = pow(10,n);
printf("%d %d\n" , x , y);
printf("%f %f\n" , k , l);
}
Output : 100 99
100.000000 100.000000
Update 2 Assembly Instructions FOR CODE1
Generated Assembly Instructions GCC 4.9.2 using gcc -S -masm=intel
:
.LC1:
.ascii "%d %d\0"
.text
.globl _main
.def _main; .scl 2; .type 32; .endef
_main:
push ebp
mov ebp, esp
and esp, -16
sub esp, 48
call ___main
mov DWORD PTR [esp+44], 2
mov DWORD PTR [esp+40], 100 //Concerned Line
fild DWORD PTR [esp+44]
fstp QWORD PTR [esp+8]
fld QWORD PTR LC0
fstp QWORD PTR [esp]
call _pow //Concerned Line
fnstcw WORD PTR [esp+30]
movzx eax, WORD PTR [esp+30]
mov ah, 12
mov WORD PTR [esp+28], ax
fldcw WORD PTR [esp+28]
fistp DWORD PTR [esp+36]
fldcw WORD PTR [esp+30]
mov eax, DWORD PTR [esp+36]
mov DWORD PTR [esp+8], eax
mov eax, DWORD PTR [esp+40]
mov DWORD PTR [esp+4], eax
mov DWORD PTR [esp], OFFSET FLAT:LC1
call _printf
leave
ret
.section .rdata,"dr"
.align 8
LC0:
.long 0
.long 1076101120
.ident "GCC: (tdm-1) 4.9.2"
.def _pow; .scl 2; .type 32; .endef
.def _printf; .scl 2; .type 32; .endef
You're not the first to find this. Here's a discussion form 2013: pow() cast to integer, unexpected result
I'm speculating that the assembly code produced by the tcc guys is causing the second value to be rounded down after calculating a result that is REALLY close to 100. Like mikijov said in that historic post, looks like the bug has been fixed.
We do not know the values are that different.
When comparing the textual out of
int/double
, be sure to print thedouble
with sufficient precision to see if it is100.000000
or just near100.000000
or in hex to remove all doubt.C does not specify the accuracy of most
<math.h>
functions. The following are all compliant results.Assigning a floating point (FP) number to an
int
simple drops the fraction regardless of how close the fraction is to 1.0When converting FP to an integer, better to control the conversion and round to cope with minor computational differences.
As others have mentioned, Code 2 returns 99 due to floating point truncation. The reason why Code 1 returns a different and correct answer is because of a libc optimization.
When the power is a small positive integer, it is more efficient to perform the operation as repeated multiplication. The simpler path removes roundoff. Since this is inlined you don't see function calls being made.
You must first, if you haven't already, divest yourself of the idea that floating-point numbers are in any way sensible or predictable.
double
only approximates real numbers and almost anything you do with adouble
is likely to be an approximation to the actual result.That said, as you have realized,
pow(10, n)
resulted in a value like99.99999999999997
, which is an approximation accurate to 15 significant figures. And then you told it to truncate to the largest integer less than that, so it threw away most of those.(Aside: there is rarely a good reason to convert a
double
to anint
. Usually you should either format it for display with something likesprintf("%.0f", x)
, which does rounding correctly, or use thefloor
function, which can handle floating-point numbers that may be out of the range of anint
. If neither of those suit your purpose, like in currency or date calculations, possibly you should not be using floating point numbers at all.)There are two weird things going on here. First, why is
pow(10, n)
inaccurate? 10, 2, and 100 are all precisely representable asdouble
. The best answer I can offer is that the C standard library you are using has a bug. (The compiler and the standard library, which I assume are gcc and glibc, are developed on different release schedules and by different teams. Ifpow
is returning inaccurate results, that is probably a bug in glibc, not gcc.)In the comments on your question, amdn found a glibc bug to do with FP rounding that might be related and another Q&A that goes into more detail about why this happens and how it's not a violation of the C standard. chux's answer also addresses this. (C doesn't require implementation of IEEE 754, but even if it did,
pow
isn't required to use correct rounding.) I will still call this a glibc bug, because it's an undesirable property.(It's also conceivable, though unlikely, that your processor's FPU is wrong.)
Second, why is
pow(10, n)
different frompow(10, 2)
? This one is far easier. gcc optimizes away function calls for which the result can be calculated at compile time, sopow(10, 2)
is almost certainly being optimized to100.0
. If you look at the generated assembly code, you will find only one call topow
.The GCC manual, section 6.59 describes which standard library functions may be treated in this way (follow the link for the full list):
So it would seem you can disable this behavior with
-fno-builtin-pow
.You've fooled it into thinking that the inputs are real and so it gives an approximate answer, which happens to be slightly under 100, e.g. 99.999999 that is then truncated to 99.