7
void main()
{
    float f = 0.98;
    if(f <= 0.98)
        printf("hi");
    else
        printf("hello");
    getch();
}

I am getting this problem here.On using different floating point values of f i am getting different results. Why this is happening?

Adi
  • 163
  • 2
  • 4
  • 8
  • General rule: don't you ever compare floating-point numbers in an 'exact' fashion. It just doesn't make sense. Always use some 'epsilon' – valdo Oct 19 '10 at 01:14
  • 4
    @valdo: That's generally bad advice without a much more thorough analysis. – R.. GitHub STOP HELPING ICE Oct 19 '10 at 02:58
  • `void main()` is wrong. `int main(void)` is correct. – Keith Thompson Sep 22 '13 at 20:53
  • 1
    @KeithThompson: Unfortunately, you need to see [What should `main()` return in C and C++?](http://stackoverflow.com/questions/204476/what-should-main-return-in-c-and-c/18721336#18721336). – Jonathan Leffler Sep 22 '13 at 21:51
  • @JonathanLeffler: Arrgh! I didn't know that Microsoft actually documented `void main(void)` as an "other implementation-defined manner" as specified in 5.1.2.2.1 of the C standard. I'd still argue that `void main(void)` is acceptable *under Microsoft C*, but `void main()` is not (insufficient room here for the language-lawyerly argument). There is still absolutely no good reason to define `main` with a `void` return type unless you're using a freestanding implementation. – Keith Thompson Sep 22 '13 at 22:09
  • 1
    @KeithThompson: I was about as chagrined as you sound when I found the MSVS documentation for 2008 onwards documents it — which was only a week or two ago. I agree that `int main(void)` is preferable, but I'd be curious to hear the expanded version of why, as a function definition, `int main()` is not (email in profile). The GCC compiler options I use mean that I use `int main(void)` or `int main(int argc, char **argv)` and not `int main()`, but that's not a language lawyerly argument. – Jonathan Leffler Sep 22 '13 at 22:32
  • 1
    @JonathanLeffler: Briefly, `int main()` is not equivalent to `int main(void)`. The latter makes `main(42)` a constraint violation; the former does not. On the other hand, `int main()` was certainly correct in pre-ANSI C, and the *intent* was to avoid breaking old code, so I'd argue that `int main()` *should* be permitted (but obsolescent), but IMHO the current wording of the Standard doesn't say so. – Keith Thompson Sep 22 '13 at 22:49
  • @KeithThompson: I see what you mean! It's only a problem if you call `main()` from somewhere in the file you define it (called from outside, you don't get any support from the implementation — it is not allowed to declare a prototype for `main()`). Of course, C++ doesn't allow the user to call `main()` at all, and in any case in C++ `int main()` means a function with no arguments, but C is subtly different. – Jonathan Leffler Sep 22 '13 at 22:53

2 Answers2

22

f is using float precision, but 0.98 is in double precision by default, so the statement f <= 0.98 is compared using double precision.

The f is therefore converted to a double in the comparison, but may make the result slightly larger than 0.98.

Use

if(f <= 0.98f)

or use a double for f instead.


In detail... assuming float is IEEE single-precision and double is IEEE double-precision.

These kinds of floating point numbers are stored with base-2 representation. In base-2 this number needs an infinite precision to represent as it is a repeated decimal:

0.98 = 0.1111101011100001010001111010111000010100011110101110000101000...

A float can only store 24 bits of significant figures, i.e.

       0.111110101110000101000111_101...
                                 ^ round off here
   =   0.111110101110000101001000

   =   16441672 / 2^24

   =   0.98000001907...

A double can store 53 bits of signficant figures, so

       0.11111010111000010100011110101110000101000111101011100_00101000...
                                                              ^ round off here
   =   0.11111010111000010100011110101110000101000111101011100

   =   8827055269646172 / 2^53

   =   0.97999999999999998224...

So the 0.98 will become slightly larger in float and smaller in double.

kennytm
  • 510,854
  • 105
  • 1,084
  • 1,005
3

It's because floating point values are not exact representations of the number. All base ten numbers need to be represented on the computer as base 2 numbers. It's in this conversion that precision is lost.

Read more about this at http://en.wikipedia.org/wiki/Floating_point


An example (from encountering this problem in my VB6 days)

To convert the number 1.1 to a single precision floating point number we need to convert it to binary. There are 32 bits that need to be created.

Bit 1 is the sign bit (is it negative [1] or position [0]) Bits 2-9 are for the exponent value Bits 10-32 are for the mantissa (a.k.a. significand, basically the coefficient of scientific notation )

So for 1.1 the single floating point value is stored as follows (this is truncated value, the compiler may round the least significant bit behind the scenes, but all I do is truncate it, which is slightly less accurate but doesn't change the results of this example):

s --exp--- -------mantissa--------
0 01111111 00011001100110011001100

If you notice in the mantissa there is the repeating pattern 0011. 1/10 in binary is like 1/3 in decimal. It goes on forever. So to retrieve the values from the 32-bit single precision floating point value we must first convert the exponent and mantissa to decimal numbers so we can use them.

sign = 0 = a positive number

exponent: 01111111 = 127

mantissa: 00011001100110011001100 = 838860

With the mantissa we need to convert it to a decimal value. The reason is there is an implied integer ahead of the binary number (i.e. 1.00011001100110011001100). The implied number is because the mantissa represents a normalized value to be used in the scientific notation: 1.0001100110011.... * 2^(x-127).

To get the decimal value out of 838860 we simply divide by 2^-23 as there are 23 bits in the mantissa. This gives us 0.099999904632568359375. Add the implied 1 to the mantissa gives us 1.099999904632568359375. The exponent is 127 but the formula calls for 2^(x-127).

So here is the math:

(1 + 099999904632568359375) * 2^(127-127)

1.099999904632568359375 * 1 = 1.099999904632568359375

As you can see 1.1 is not really stored in the single floating point value as 1.1.

Matt
  • 14,353
  • 5
  • 53
  • 65