Mayank S
asked on
If/ Else problem (with floating-point numbers)
Hi folks! Can anybody guess the output of the following code?
#include <stdio.h>
void main ()
{
float a = 0.6 ;
if ( a < 0.6 )
printf ( "\n Lesser. " ) ; // end if
else
printf ( "\n Greater or equal. " ) ; // end else
a = 0.7 ;
if ( a < 0.7 )
printf ( "\n Lesser. " ) ; // end if
else
printf ( "\n Greater or equal. " ) ; // end else
} // end of main ()
OUTPUT:
Greater or equal.
Lesser.
???? Can anybody explain why this happened (on Turbo C++ 3.0/ Windows 2000)? However, changing the data type of 'a' to 'double' gives the expected output:
Greater or equal.
Greater or equal.
When defined as a 'float', the branching changes after a value of 0.649299 as per what I've tested. Any explanations??
Mayank.
#include <stdio.h>
void main ()
{
float a = 0.6 ;
if ( a < 0.6 )
printf ( "\n Lesser. " ) ; // end if
else
printf ( "\n Greater or equal. " ) ; // end else
a = 0.7 ;
if ( a < 0.7 )
printf ( "\n Lesser. " ) ; // end if
else
printf ( "\n Greater or equal. " ) ; // end else
} // end of main ()
OUTPUT:
Greater or equal.
Lesser.
???? Can anybody explain why this happened (on Turbo C++ 3.0/ Windows 2000)? However, changing the data type of 'a' to 'double' gives the expected output:
Greater or equal.
Greater or equal.
When defined as a 'float', the branching changes after a value of 0.649299 as per what I've tested. Any explanations??
Mayank.
When you use a float, the 4 bytes used to store the number is not large enough to hold the .7 accurately. The closes that it can come is .69xxxxx. A double, however, can represent a lot more decimal places in it's 8 bytes. Therefore, the number is close enough to .7 to give the expected results in the if statement.
In short, a double has 15 digits of precision, whereas a float only has only 7 digits.
Well, my lecturer had a strong opinion about this.
Comparing floating point like these
a == b
a < b
a > b
are FORBIDDEN. I would get automatic E for that mistake.
The reason is floating point numbers should have error. And the best way to compare is
(a-b) < ERROR
So man, I can only say ... you just prove that she was right.
Comparing floating point like these
a == b
a < b
a > b
are FORBIDDEN. I would get automatic E for that mistake.
The reason is floating point numbers should have error. And the best way to compare is
(a-b) < ERROR
So man, I can only say ... you just prove that she was right.
I wont' give you big explanation. The only thing I want to tell you is that with floating point numbers, conditional operations are very unpredictable because of truncation error and all. To clarify all this stuff consult any topic in numerical computations. Dont' have any site address, sorry for that.
Hope this may help you.
thanx
Hope this may help you.
thanx
ASKER
Dear mnashadka,
Are there are any reasons why it can hold uptil 0.649299 accurately and then not store values larger than 0.7? As such, a floating-point value is supposed to fit within 4 bytes, right? And a number as small as 0.65 itself cannot be stored with precision in those 4 bytes?
Dear Kocil,
'ERROR' is not defined in Turbo C++ - I couldn't find it in the Help library either. But let's say we do write:
if ( ( a - b ) < ERROR )
--> ( a - b ) will yield a floating-point value as a result, and we're again still two floating point values in the if statement, aren't we?
Mayank.
Are there are any reasons why it can hold uptil 0.649299 accurately and then not store values larger than 0.7? As such, a floating-point value is supposed to fit within 4 bytes, right? And a number as small as 0.65 itself cannot be stored with precision in those 4 bytes?
Dear Kocil,
'ERROR' is not defined in Turbo C++ - I couldn't find it in the Help library either. But let's say we do write:
if ( ( a - b ) < ERROR )
--> ( a - b ) will yield a floating-point value as a result, and we're again still two floating point values in the if statement, aren't we?
Mayank.
We've not yet heard part of the explanation needed to understand the program's behavior.
In arithmetic expressions, operands of type float are converted to type double before doing the operation. This is a convention in C that can often be overridden by compile-time flags, but the default is to convert to double.
Now, when your 'a' variable was of type float, and you compared it with 0.7, the approximation to 0.7 that was stored in 'a' was simply extended with 0 bits to make it into a double. The operand on the other side, 0.7, was converted with full double-precision accuracy. For numbers that cannot be represented exactly in single-precision, comparing the single-precision version with the double-precision one will always result in a discrepancy, as you've discovered.
In arithmetic expressions, operands of type float are converted to type double before doing the operation. This is a convention in C that can often be overridden by compile-time flags, but the default is to convert to double.
Now, when your 'a' variable was of type float, and you compared it with 0.7, the approximation to 0.7 that was stored in 'a' was simply extended with 0 bits to make it into a double. The operand on the other side, 0.7, was converted with full double-precision accuracy. For numbers that cannot be represented exactly in single-precision, comparing the single-precision version with the double-precision one will always result in a discrepancy, as you've discovered.
Oops, are you making something out of this ?
I though you were only puzzling us.
So, this is what you should do.
Define the error by yourself as required (the smaller the better, but there is limit)
#define ERROR 0.0000001
then change the expresion
a<b --> a-b > ERROR
a>b --> b-a > ERROR
a==b --> abs(a-b) < ERROR
cheers
I though you were only puzzling us.
So, this is what you should do.
Define the error by yourself as required (the smaller the better, but there is limit)
#define ERROR 0.0000001
then change the expresion
a<b --> a-b > ERROR
a>b --> b-a > ERROR
a==b --> abs(a-b) < ERROR
cheers
ASKER
Kocil,
Yeah! I'd tried a similar thing a little while after posting my last comment. But it still doesn't work - we're still comparing floating-point values, right?
Mayank.
Yeah! I'd tried a similar thing a little while after posting my last comment. But it still doesn't work - we're still comparing floating-point values, right?
Mayank.
Maybe your ERROR is too small. As I said it has limit :)
// Three significance digit after dot
// the better term is EPSILON, not ERROR :)
#define EPSILON 0.0005
main()
{
float a = 0.6 ;
if ( (a-0.6) < EPSILON) )
printf ( "\n Lesser. " ) ; // end if
else
printf ( "\n Greater or equal. " );
}
// Three significance digit after dot
// the better term is EPSILON, not ERROR :)
#define EPSILON 0.0005
main()
{
float a = 0.6 ;
if ( (a-0.6) < EPSILON) )
printf ( "\n Lesser. " ) ; // end if
else
printf ( "\n Greater or equal. " );
}
ASKER
Kocil,
Yeah! I agree the better term is EPSILON. But still, it doesn't work with a 0.0005 or with 0.001 either! Did you get the output??
Yeah! I agree the better term is EPSILON. But still, it doesn't work with a 0.0005 or with 0.001 either! Did you get the output??
ASKER
Kocil,
Yeah! I agree the better term is EPSILON. But still, it doesn't work with a 0.0005 or with 0.001 either! Did you get the output??
Yeah! I agree the better term is EPSILON. But still, it doesn't work with a 0.0005 or with 0.001 either! Did you get the output??
Personally, I would use something like:
#define EPSILON 0.0005 /* Or whatever is suitable for the application*/
main()
{
float a = 0.6 ;
if (a < (0.6 + EPSILON) )
printf ( "\n Lesser. " ) ;
else
printf ( "\n Greater or equal. " );
}
Be forewarned that numbers up to EPSILON greater than the compared value will still be considered "Lesser".
I had run the following code snippet and found that a lot of numbers are equal to 0.7! (Note: on my system, floats and ints are both 4 bytes long.)
int i;
union ab {
float a;
int b;
} test;
test.a = 0.7;
printf("a = %f, b = 0x%x\n",test.a,test.b);
test.b -= 10;
for (i = 0; i < 20; i++) {
test.b += 1;
printf("i = %2d, a = %f, b = 0x%x\n",i,test.a,test.b);
}
#define EPSILON 0.0005 /* Or whatever is suitable for the application*/
main()
{
float a = 0.6 ;
if (a < (0.6 + EPSILON) )
printf ( "\n Lesser. " ) ;
else
printf ( "\n Greater or equal. " );
}
Be forewarned that numbers up to EPSILON greater than the compared value will still be considered "Lesser".
I had run the following code snippet and found that a lot of numbers are equal to 0.7! (Note: on my system, floats and ints are both 4 bytes long.)
int i;
union ab {
float a;
int b;
} test;
test.a = 0.7;
printf("a = %f, b = 0x%x\n",test.a,test.b);
test.b -= 10;
for (i = 0; i < 20; i++) {
test.b += 1;
printf("i = %2d, a = %f, b = 0x%x\n",i,test.a,test.b);
}
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
Dawg1,
Yeah! Even I found out that 0.7 is not equal to itself.
As for Kocil,
Thanks for your efforts, friend!
[Akshay, are you there, pal??]
Mayank.
Yeah! Even I found out that 0.7 is not equal to itself.
As for Kocil,
Thanks for your efforts, friend!
[Akshay, are you there, pal??]
Mayank.
kocil said:
>>Oops, are you making something out of this ?
>>I though you were only puzzling us.
same thought came to my mind when i saw the question first. and when i came back after couple of hours , there were already lots of comments , which i found good enough for the explanation .. but now since u have called for me , here are my views , which is not much different from what already has been said
In many compilers literal floating point values (such as your 0.7) are interpreted to be of type double in order to have a high degree of precision.
When this double value gets assigned to a float variable (which has less precesion than a double), there is possible loss of precesion
float can have only 7 digits(after decimal) of precesion.
so when u do
a=0.7;
0.7 has accurate 'const double' value. while in the process of doing the assignment .. u loose some bits
now if u have lost bits .. that doesnt mean the float value will be less ... it should depends on what bits are up in the original const-double value .. (try printing the binary representation of the double or corresponding float)
so in some cases of assignment the float is effectively more than the original 'const double' ( in the case of a=0.6 )
and in some cases the float value is less than the double value ( in case of a=0.7)
look at this
a=0.6;
printf("then %2.15lf\n",0.6);
printf("now %2.15lf\n\n",a);
a = 1.123456789 ;
printf("then %2.15lf\n",1.123456789);
printf("now %2.15lf\n\n",a);
a=0.7;
printf("then %2.15lf\n",0.7);
printf("now %2.15lf\n\n",a);
the output is
then 0.600000000000000
now 0.600000023841858
then 1.123456789000000
now 1.123456835746765
then 0.700000000000000
now 0.699999988079071
u can also observe the 7-digit precision concept .. ( upto 6th decimal place values are same)
BY THE WAY if u do
float a=0.7;
then if u do
if(a==0.7) printf("equal\n");
else printf("not equal");
i dont think u need to guess what will be the answer ..
i hope that explains your question ..
like kocil I also hope it wasnt meant to be a puzzle..
Akshay
>>Oops, are you making something out of this ?
>>I though you were only puzzling us.
same thought came to my mind when i saw the question first. and when i came back after couple of hours , there were already lots of comments , which i found good enough for the explanation .. but now since u have called for me , here are my views , which is not much different from what already has been said
In many compilers literal floating point values (such as your 0.7) are interpreted to be of type double in order to have a high degree of precision.
When this double value gets assigned to a float variable (which has less precesion than a double), there is possible loss of precesion
float can have only 7 digits(after decimal) of precesion.
so when u do
a=0.7;
0.7 has accurate 'const double' value. while in the process of doing the assignment .. u loose some bits
now if u have lost bits .. that doesnt mean the float value will be less ... it should depends on what bits are up in the original const-double value .. (try printing the binary representation of the double or corresponding float)
so in some cases of assignment the float is effectively more than the original 'const double' ( in the case of a=0.6 )
and in some cases the float value is less than the double value ( in case of a=0.7)
look at this
a=0.6;
printf("then %2.15lf\n",0.6);
printf("now %2.15lf\n\n",a);
a = 1.123456789 ;
printf("then %2.15lf\n",1.123456789);
printf("now %2.15lf\n\n",a);
a=0.7;
printf("then %2.15lf\n",0.7);
printf("now %2.15lf\n\n",a);
the output is
then 0.600000000000000
now 0.600000023841858
then 1.123456789000000
now 1.123456835746765
then 0.700000000000000
now 0.699999988079071
u can also observe the 7-digit precision concept .. ( upto 6th decimal place values are same)
BY THE WAY if u do
float a=0.7;
then if u do
if(a==0.7) printf("equal\n");
else printf("not equal");
i dont think u need to guess what will be the answer ..
i hope that explains your question ..
like kocil I also hope it wasnt meant to be a puzzle..
Akshay
Ohh forgot to emphasize, for the reasons that I and others mentioned .. one should be using EPSILON thing.. when dealing with floats
otherwise forget everything and use doubles, as anyway ur float values will be promoted to double in comparisons.. :P
otherwise forget everything and use doubles, as anyway ur float values will be promoted to double in comparisons.. :P
ASKER
Thanks for your comments, Akshay. It was just that different outputs on different platforms were creating confusion. One of my friends tried this on UNIX and on his system, the output was changing for some different value like 0.6993599. Implementation differences.
Anyways, thanks everybody for your time!
Mayank.
Anyways, thanks everybody for your time!
Mayank.
ASKER
Good job, Kocii! Thanks!