?
Solved

If/ Else problem (with floating-point numbers)

Posted on 2003-03-12
18
Medium Priority
?
346 Views
Last Modified: 2010-04-15
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.
0
Comment
Question by:Mayank S
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 7
  • 4
  • 2
  • +4
18 Comments
 
LVL 8

Expert Comment

by:mnashadka
ID: 8125265
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.
0
 
LVL 8

Expert Comment

by:mnashadka
ID: 8125282
In short, a double has 15 digits of precision, whereas a float only has only 7 digits.
0
 
LVL 5

Expert Comment

by:Kocil
ID: 8125360
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.



0
VIDEO: THE CONCERTO CLOUD FOR HEALTHCARE

Modern healthcare requires a modern cloud. View this brief video to understand how the Concerto Cloud for Healthcare can help your organization.

 
LVL 12

Expert Comment

by:rajeev_devin
ID: 8125387
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
0
 
LVL 30

Author Comment

by:Mayank S
ID: 8125404
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.
0
 
LVL 20

Expert Comment

by:jmcg
ID: 8125434
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.
0
 
LVL 5

Expert Comment

by:Kocil
ID: 8125507
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

0
 
LVL 30

Author Comment

by:Mayank S
ID: 8125635
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.
0
 
LVL 5

Expert Comment

by:Kocil
ID: 8125891
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. " );
}
0
 
LVL 30

Author Comment

by:Mayank S
ID: 8125928
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??
0
 
LVL 30

Author Comment

by:Mayank S
ID: 8125994
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??
0
 

Expert Comment

by:dawg1
ID: 8126077
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);
   }

0
 
LVL 5

Accepted Solution

by:
Kocil earned 200 total points
ID: 8126496
This is my program
#include <stdio.h>

#define EPSILON 0.005

int fcomp(float a, float b)
{
     if (a-EPSILON > b) return 1;
     if (a+EPSILON < b) return -1;
     return 0;
}



main()
{
     int i=0;
     float a = 0.59;
     float b = 0.6;

     printf("\nEPSILON=%6.4f\n", EPSILON);
     printf("a     a+E    a-E    b      compare\n");
     for (i=0; i<20; i++) {
        printf("%1.3f %1.3f  %1.3f  %1.3f  %d\n",
                a, a+EPSILON, a-EPSILON, b, fcomp(a, b));
        a += 0.001;
     }
}


The result looks fine

EPSILON=0.0050
a     a+E    a-E    b      compare
0.590 0.595  0.585  0.600  -1
0.591 0.596  0.586  0.600  -1
0.592 0.597  0.587  0.600  -1
0.593 0.598  0.588  0.600  -1
0.594 0.599  0.589  0.600  -1
0.595 0.600  0.590  0.600  -1
0.596 0.601  0.591  0.600  0
0.597 0.602  0.592  0.600  0
0.598 0.603  0.593  0.600  0
0.599 0.604  0.594  0.600  0
0.600 0.605  0.595  0.600  0
0.601 0.606  0.596  0.600  0
0.602 0.607  0.597  0.600  0
0.603 0.608  0.598  0.600  0
0.604 0.609  0.599  0.600  0
0.605 0.610  0.600  0.600  0
0.606 0.611  0.601  0.600  1
0.607 0.612  0.602  0.600  1
0.608 0.613  0.603  0.600  1
0.609 0.614  0.604  0.600  1
0
 
LVL 30

Author Comment

by:Mayank S
ID: 8126565
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.
0
 
LVL 8

Expert Comment

by:akshayxx
ID: 8127045
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
0
 
LVL 8

Expert Comment

by:akshayxx
ID: 8127059
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
0
 
LVL 30

Author Comment

by:Mayank S
ID: 8127112
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.
0
 
LVL 30

Author Comment

by:Mayank S
ID: 8127128
Good job, Kocii! Thanks!
0

Featured Post

Technology Partners: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

This tutorial is posted by Aaron Wojnowski, administrator at SDKExpert.net.  To view more iPhone tutorials, visit www.sdkexpert.net. This is a very simple tutorial on finding the user's current location easily. In this tutorial, you will learn ho…
Windows programmers of the C/C++ variety, how many of you realise that since Window 9x Microsoft has been lying to you about what constitutes Unicode (http://en.wikipedia.org/wiki/Unicode)? They will have you believe that Unicode requires you to use…
Video by: Grant
The goal of this video is to provide viewers with basic examples to understand and use nested-loops in the C programming language.
The goal of this video is to provide viewers with basic examples to understand opening and reading files in the C programming language.
Suggested Courses

777 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question