Link to home
Start Free TrialLog in
Avatar of Mayank S
Mayank SFlag for India

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.
Avatar of mnashadka
mnashadka

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.



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
Avatar of Mayank S

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.
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.
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

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.
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. " );
}
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??
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??
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);
   }

ASKER CERTIFIED SOLUTION
Avatar of Kocil
Kocil

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
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.
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
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
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.
Good job, Kocii! Thanks!