C/Strange behaviour in Floating Poing Arithmetic
Expert: Narendra - 2/21/2006
QuestionHi,
I came across some strange behaviour in dealing with floating point arithmetic in a C code. The code is given below.
#include <stdio.h>
#include <stdlib.h>
double roundvalues1( double , double );
double roundvalues2( double , double );
void main(int argc, char *argv[])
{
double v1=0.00,
v2=0.00;
v1 = 4749.70;
v2 = 0.15;
fprintf(stdout,"\n------------------------------------------------------------\n\n\n");
fprintf(stdout,"1---Sending to roundvalues1---\n");
fprintf(stdout,"\n1.Rounded Value for [%f X %f]is :[%f]\n",v1,v2,roundvalues1(v1,v2));
fprintf(stdout,"1---End of roundvalues1---\n\n");
fprintf(stdout,"1---Sending to roundvalues2---\n");
fprintf(stdout,"\n1.Rounded Value for [%f X %f]is :[%f]\n",v1,v2,roundvalues2(v1,v2));
fprintf(stdout,"1---End of roundvalues2---\n\n\n");
fprintf(stdout,"------------------------------------------------------------\n\n\n");
v1 = 712.455;
v2 = 1;
fprintf(stdout,"2---Sending to roundvalues1---\n");
fprintf(stdout,"\n2.Rounded Value for [%f X %f]is :[%f]\n",v1,v2,roundvalues1(v1,v2));
fprintf(stdout,"2---End of roundvalues1---\n\n");
fprintf(stdout,"2---Sending to roundvalues2---\n");
fprintf(stdout,"\n2.Rounded Value for [%f X %f]is :[%f]\n",v1,v2,roundvalues2(v1,v2));
fprintf(stdout,"2---End of roundvalues2---\n");
fprintf(stdout,"\n------------------------------------------------------------\n\n");
}
double roundvalues2( double val1, double val2)
{
char c_charge[80];
double d_charge=0.00;
long l_charge=0;
d_charge=(val1*val2);
printf("\td_charge after d_charge=(val1*val2 ) is [%f]\n",d_charge);
if ((d_charge-712.455)!=0)
{
printf("\t1.difference of d_charge-712.455 is [%f]\n",(d_charge-712.455));
if(d_charge > 712.455)
printf("\t\td_charge > 712.455\n");
else if(d_charge < 712.455)
printf("\t\td_charge < 712.455\n");
}
l_charge = d_charge*10000;
printf("\t1_charge after l_charge = d_charge*10000.00 is [%ld]\n",l_charge);
d_charge=0.00;
d_charge = l_charge/10000.00;
printf("\td_charge after d_charge = d_charge/10000.00 is [%f]\n",d_charge);
if ((d_charge-712.455)!=0)
{
printf("\t2.difference of d_charge-712.455 is [%f]\n",(d_charge-712.455));
}
c_charge[0]='\0';
sprintf(c_charge,"%.2f",d_charge);
printf("\tc_charge after sprintf is [%s]\n",c_charge);
d_charge=atof(c_charge);
printf("\td_charge after d_charge=atof(c_charge) is [%f]\n",d_charge);
return(d_charge);
}
double roundvalues1( double val1, double val2)
{
char c_charge[80];
double d_charge=0.00;
d_charge=val1*val2;
printf("\td_charge after d_charge=(val1*val2 ) is [%f]\n",d_charge);
sprintf(c_charge,"%.2f",d_charge);
printf("\tc_charge after sprintf is [%s]\n",c_charge);
d_charge=0.00;
d_charge=atof(c_charge);
printf("\td_charge after d_charge=atof(c_charge) is [%f]\n",d_charge);
return(d_charge);
}
And the output is given below.
------------------------------------------------------------
1---Sending to roundvalues1---
d_charge after d_charge=(val1*val2 ) is [712.455000]
c_charge after sprintf is [712.45]
d_charge after d_charge=atof(c_charge) is [712.450000]
1.Rounded Value for [4749.700000 X 0.150000]is :[712.450000]
1---End of roundvalues1---
1---Sending to roundvalues2---
d_charge after d_charge=(val1*val2 ) is [712.455000]
1.difference of d_charge-712.455 is [-0.000000]
d_charge < 712.455
1_charge after l_charge = d_charge*10000.00 is [7124549]
d_charge after d_charge = d_charge/10000.00 is [712.454900]
2.difference of d_charge-712.455 is [-0.000100]
c_charge after sprintf is [712.45]
d_charge after d_charge=atof(c_charge) is [712.450000]
1.Rounded Value for [4749.700000 X 0.150000]is :[712.450000]
1---End of roundvalues2---
------------------------------------------------------------
2---Sending to roundvalues1---
d_charge after d_charge=(val1*val2 ) is [712.455000]
c_charge after sprintf is [712.46]
d_charge after d_charge=atof(c_charge) is [712.460000]
2.Rounded Value for [712.455000 X 1.000000]is :[712.460000]
2---End of roundvalues1---
2---Sending to roundvalues2---
d_charge after d_charge=(val1*val2 ) is [712.455000]
1_charge after l_charge = d_charge*10000.00 is [7124550]
d_charge after d_charge = d_charge/10000.00 is [712.455000]
c_charge after sprintf is [712.46]
d_charge after d_charge=atof(c_charge) is [712.460000]
2.Rounded Value for [712.455000 X 1.000000]is :[712.460000]
2---End of roundvalues2---
------------------------------------------------------------
Basically what I'm trying to do is this. I have two double variables. I want to multiply these two and get the answer rounded to two decimal places. So here's where the roundvalues1 fucntion come in.
It basically multiplies the two incoming parameters and using an sprintf() formats it to a value with 2 decimal places and then converts back to a double and returns.
Now if you take the two sets of example values that I have taken;
4749.7 X 0.15 = 712.455
and
712.455 X 1 = 712.455
As you see in the output, after the sprintf();
4749.7 X 0.15 = 712.455 becomes 712.45
and
712.455 X 1 = 712.455 becomes 712.46
How can this behaviour be explained? And what is the solution for this? ( i mean to keep this consistant)
------------------------------------
In the roundvalues2 function I have taken a different approach. I've multiplied the the answer I get from multiplying the two parameters by 10000 to get rid of the decimal point and then divided it by 10000.00 and sent to sprintf().
In the meantime I've cheked whether the multiplied outcome of (4749.7 X 0.15) is equal to 712.455. Bu t as it appered to be it's not. And strangely enought it's less than 712.455.
Furthermore when the answer of this is multiplied by 10000 and assigned to a long variable the answer becomes 7124549. How can that be?
There it explains the rest of the senario. But how it became 7124549 is a mystry to me.
Can you please give an explernation tp this behaviour? And is there a way to rectify this?
I've tried this in Red hat linux Advanced Server and Red hat linux 7.1 using the gcc compiler. Also I've tried this in Turbo C in Windows XP as well.
Appreciate a lot if you can get this clarified.
Thanks
Nivantha
AnswerThe reason for this is, the way floating point number is represented inside your computer.
It is not represented the way you see on your screen.
It is represented in the way defined by IEEE standards.
It sets aside some part of the storage to mantissa, some part to exponent, etc.
If you want to truncate, you can use the trunc() or floor() functions.
-Narendra