66
int main()
{
    float a = 0.7;
    float b = 0.5;
    if (a < 0.7)
    {
       if (b < 0.5) printf("2 are right");
       else         printf("1 is right");
    }
    else printf("0 are right");
}

I would have expected the output of this code to be 0 are right. But to my dismay the output is 1 is right why?

Pieniadz
  • 653
  • 3
  • 9
  • 22
sasidhar
  • 7,523
  • 15
  • 49
  • 75
  • 5
    0.7 isnt a float but a double. That might be a reason for that behavior – Sebastian Hoffmann Aug 10 '11 at 13:05
  • 2
    One reason is that `a` is a `float` and `.7` is a `double`. – Bo Persson Aug 10 '11 at 13:05
  • 4
    Mitch, if `0.7 == 0.7` the answer would be `0 are right` – Emil Vikström Aug 10 '11 at 13:06
  • The first if and second if should get evaluated the same way right?? so the answer should either be `2 are right` or `0 are right` but why `1 is right`?? – sasidhar Aug 10 '11 at 13:06
  • 1
    good point. It's late. I should stop now!..... – Mitch Wheat Aug 10 '11 at 13:07
  • 1
    the mistery is his output. 0.7 == 0.7, so he would have expected to go into the else. But... he didn't :) 3 people out of 4 doesn't seem to understand his question... – Samuele Mattiuzzo Aug 10 '11 at 13:09
  • writing `if (a <.7f)` would've been more correct, faster and maybe given less compiler warnings (if the right warning level was used). – Macke Aug 10 '11 at 15:11
  • 29
    You should read ["What every computer scientist should know about floating-point arithmetic"](http://download.oracle.com/docs/cd/E19957-01/806-3568/ncg_goldberg.html). – Andrea Bergia Aug 10 '11 at 13:03
  • An interesting concept. If the compiler could track what exactly was put into a float, given no computation was performed, it could possibly remember this and do the comparisons correctly. Good luck knowing that for sure though. One way would be to keep a chart of hard coded numbers in order from least to greatest, and simply put a reference of that number into the float instead. Then whenever a comparison occured, the item could be correctly compared regardless of precision limitations at runtime. – Lee Louviere Aug 10 '11 at 20:51
  • Your compiler warned you when you stored 0.7 in a float, right? – Russell Borogove Aug 10 '11 at 21:28
  • The two 0.7's do not receive the same treatment. The first one is put into a float variable (a), while the second one is left as a literal. That can make a difference, as you will find out when you read the recommended material. Maybe you should try: float a = 0.7; float valueToWhichIAmGoingToCompare = 0.7; if (a < valueToWhichIAmGoingToCompare)... (Disclaimer: I made this comment before realizing that the question is already well answered.) – Daniel Daranas Apr 29 '13 at 10:09

5 Answers5

132
int main()
{
    float a = 0.7, b = 0.5; // These are FLOATS
    if(a < .7)              // This is a DOUBLE
    {
      if(b < .5)            // This is a DOUBLE
        printf("2 are right");
      else
        printf("1 is right");
    }
    else
      printf("0 are right");
}

Floats get promoted to doubles during comparison, and since floats are less precise than doubles, 0.7 as float is not the same as 0.7 as double. In this case, 0.7 as float becomes inferior to 0.7 as double when it gets promoted. And as Christian said, 0.5 being a power of 2 is always represented exactly, so the test works as expected: 0.5 < 0.5 is false.

So either:

  • Change float to double, or:
  • Change .7 and .5 to .7f and .5f,

and you will get the expected behavior.

user703016
  • 37,307
  • 8
  • 87
  • 112
  • 1
    The first if and second if should get evaluated the same way right?? so the answer should either be `2 are right` or `0 are right` but why `1 is right`?? – sasidhar Aug 10 '11 at 13:08
  • I edited my answer to explain why. – user703016 Aug 10 '11 at 13:10
  • 1
    sasidhar, they are different numbers so they may lose precision in different ways. – Emil Vikström Aug 10 '11 at 13:10
  • @sasidhar 0.5 is always representable exactly (assuming IEEE), so there is no difference between the float and double versions. – Christian Rau Aug 10 '11 at 13:11
  • Is there a way to understand when these floats will exactly be promoted which way? I mean 0.7 is inferior but 0.5 is superior. Is this behavior unpredictable? Given such a question what should be my answer? Unpredictable? – sasidhar Aug 10 '11 at 13:13
  • 5
    +1 For the only answer that didn't call out "never compare floats" when seeing the question and actually thought about what is really going on here. – Christian Rau Aug 10 '11 at 13:13
  • 43
    The behavior is 100% predictable assuming IEEE 754 floating point. `0.7` becomes the nearest `double` (0.6999999999999999555910790149937383830547332763671875) and converting that to `float` rounds to the nearest `float` value (0.699999988079071044921875). – R.. GitHub STOP HELPING ICE Aug 10 '11 at 14:28
  • @R: Thought about writing an answer? :) –  Aug 10 '11 at 14:52
  • @sasidhar: It is easy to predicate when types get promoted. **ALL** operations (on normal types) are done on operands of the same type. Thus <,<=,>=,>,+,-,*,/ etc both sides of the operator are converted to the same type before the operation happens. Thus `0.5 < 1` As types is ` < ` Thus the compiler will convert the RHS to double to make sure both sides are the same type. `0.5 < 1.0` – Martin York Aug 10 '11 at 15:34
  • @Martin Its quite predictable when they get promoted. I agree. But is it predictable when these get evaluated which way? At one point ` < ` evaluated to true, while at other, it evaluate to false. Is this behavior predictable? – sasidhar Aug 10 '11 at 15:38
  • 1
    @sasidhar: Yes it is predictable. But you have to know the binary representation of the numbers. [Floating Point format](http://en.wikipedia.org/wiki/Single_precision_floating-point_format). Here 0.5 is stored precisely so its representation in float is converted to double with no loss of information. Unfortunately 0.7 is not represented precisely as float. So when you assign it to the variable 'a' information is lost. When this value is promoted to a double you can not re-create this lost information. – Martin York Aug 10 '11 at 15:50
  • 3
    @sasidhar: 0.7 represented as float is stored as `0.699999988079071044921875` and stored as a double is `0.699999999999999955591079014994` So there is a difference of `0.000000011920928910669204014994` which is lost during the assignment to `a` That information can not be re-created when `a` is converted to a double. – Martin York Aug 10 '11 at 16:10
  • 4
    @sasidhar: You should assume that all floating point numbers will not be stored precisely and their will be some loss of information. (thus comparison of floating point values should not be done precisely but to a margin of error). Note if the variables get optimized out and all the values remain in registers for the duration of the program then you may get a different result. Try compiling in release mode with the optimizer turned to its highest level and see if the results change. – Martin York Aug 10 '11 at 16:15
  • This answer is good. While .7 and .2 are exact in base-10 floating-point, they are approximate in base-2 floating-point, and the error is different for different values and different for float and double. I blogged about this precise issue, to distinguish between this and the general problem of floating-point comparisons. Here: http://randomascii.wordpress.com/2012/06/26/doubles-are-not-floats-so-dont-compare-them/ – Bruce Dawson Jul 13 '14 at 23:05
14

The issue is that the constants you are comparing to are double not float. Also, changing your constants to something that is representable easily such as a factor of 5 will make it say 0 is right. For example,

main()
{
    float a=0.25,b=0.5; 
    if(a<.25) 
       {
       if(b<.5) 
               printf("2 are right");
       else
               printf("1 is right");
       }
else
printf("0 are right");
}

Output:

0 are right

This SO question on Most Effective Way for float and double comparison covers this topic.

Also, this article at cygnus on floating point number comparison gives us some tips:

The IEEE float and double formats were designed so that the numbers are “lexicographically ordered”, which – in the words of IEEE architect William Kahan means “if two floating-point numbers in the same format are ordered ( say x < y ), then they are ordered the same way when their bits are reinterpreted as Sign-Magnitude integers.”

This means that if we take two floats in memory, interpret their bit pattern as integers, and compare them, we can tell which is larger, without doing a floating point comparison. In the C/C++ language this comparison looks like this:

if (*(int*)&f1 < *(int*)&f2)

This charming syntax means take the address of f1, treat it as an integer pointer, and dereference it. All those pointer operations look expensive, but they basically all cancel out and just mean ‘treat f1 as an integer’. Since we apply the same syntax to f2 the whole line means ‘compare f1 and f2, using their in-memory representations interpreted as integers instead of floats’.

Community
  • 1
  • 1
3

It's due to rounding issues while converting from float to double

Win32.Neshto
  • 475
  • 5
  • 6
2

Generally comparing equality with floats is a dangerous business (which is effectively what you're doing as you're comparing right on the boundary of > ), remember that in decimal certain fractions (like 1/3) cannot be expressed exactly, the same can be said of binary,

0.5= 0.1, will be the same in float or double.

0.7=0.10110011001100 etc forever, 0.7 cannot be exactly represented in binary, you get rounding errors and may be (very very slightly) different between float and double

Note that going between floats and doubles you cut off a different number of decimal places, hence your inconsistant results.

Richard Tingle
  • 16,906
  • 5
  • 52
  • 77
0

Also, btw, you have an error in your logic of 0 are right. You don't check b when you output 0 are right. But the whole thing is a little mysterious in what you are really trying to accomplish. Floating point comparisons between floats and doubles will have variations, minute, so you should compare with a delta 'acceptable' variation for your situation. I've always done this via inline functions that just perform the work (did it once with a macro, but thats too messy). Anyhow, yah, rounding issues abound with this type of example. Read the floating point stuff, and know that .7 is different than .7f and assigning .7 to a float will cast a double into a float, thus changing the exact nature of the value. But, the programming assumption about b being wrong since you checked a blared out to me, and I had to note that :)

Jay Kramer
  • 324
  • 1
  • 3