views:

386

answers:

5

Hello I have this code in C#:

float n = 2.99499989f;
MessageBox.Show("n = " + n.ToString("f2", CultureInfo.InvariantCulture));

And this code in C++:

float n = 2.99499989f;
printf("n = %.2f", n);

First one outputs 3.00.
Second one outputs 2.99.

I have no clue why this is happening.

Update:

I also tried Objective-C NSLog and the output is 2.99.

I needed to fix it fast so I used following method:

float n = 2.99499989f;
float round = (float)Math.Round(n, 2);
MessageBox.Show("round = " + round.ToString(CultureInfo.InvariantCulture));

This code shows 2.99, but computes round in double precision. I can't find Math.RoundF.

A: 

First one rounds the number incorrectly.

Second one only selects two digits after comma, I guess.

Maybe some floating-point-precission issues? 2.9949... in fact rounds to more than 2.995?

Gacek
A: 

Isn't that due to the fact that a 'float' isn't 'that' precise ?

Check this out, when you use a decimal:

        // float
        Console.WriteLine (2.99499989f.ToString ("f2"));
        // The above line outputs 3.00

        // decimal
        Console.WriteLine (2.99499989M.ToString ("f2"));
        // The above line outputs 2.99

Perhaps float can't represent 2.99499989 or whatever as 2.99.
Look also what happens when you do this:

// float
Console.WriteLine (2.98499f.ToString ("f2"));
// The above line outputs 2.99
Frederik Gheysels
What *does* happen when you use those? Can't you print the output as well as the code, so we don't have to paste it into a compiler to make sense of your answer?
jalf
A: 

The number 2.99499989f can't be presented exactly in IEEE754. Apparently printf and ToString handle this situation differently.

Maurits Rijk
As AakashM shows, the actual value stored is 2.99499988555908203125, which is more than close enough that ToString *should* give a correct answer.
jalf
Sure, but let's suppose that ToString just adds 0.005 to round to the nearest 2 decimals. In that case you get 2.99999989f which representation is 0x40400000. Which happens to be exactly 3.
Maurits Rijk
A: 

The float precision is 2^{-23} or about 0,0000001192 (relative error). Look, 2.995-2.99499989 = 0,00000011. Relative error is 3,6*10^{-8}.

If some compiler reads all digits of the 2.99499989f constant then the result is number greater than 2.995. But if another compiler reads only 2.994999 (because the precision is less than 2^{-23} and last digits are unimportant) then the result is number less than 2.995

Alexey Malistov
Not sure what you mean. How would the first compiler get `2.99499989f > 2.995`?
jalf
No problem. Compiler must choose between 2.9950001239776611328125E0 and 2.99499988555908203125. Only these values is valid by IEEE754.
Alexey Malistov
+3  A: 

Using BitConverter.GetBytes and printing out the actual bytes produced shows that this is not a compiler difference - in both cases, the actual float value stored is 0x403FAE14, which this handy calculator tells me is the exact value

2.99499988555908203125

The difference therefore must lie in differing behaviours of printf and ToString. More than that I cannot immediately say.

AakashM
+1 best link so far, thanks for handy calculator :)
Filip Kunc