4

Maybe I am missing something but the float.ToString() method rounds numbers, which causes me a lot of headache.

Take a look at the following small code: When entering 12345678 as input, the float number in the debugger is correct but the output of the ToString methods is 12345680 (in any format I've tried...)

Console.WriteLine("Please enter a float number");
float theFloat = float.Parse(Console.ReadLine());
Console.WriteLine(string.Format("You have entered {0}", theFloat.ToString("F")));

And the output:

Please enter a float number
12345678
You have entered 12345680.00

Help will be much appreciated!

Nimrod
  • 153
  • 3
  • 8
  • 5
    [What every programmer should know about floating point arithmetic](http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html) – Matthew Watson Sep 30 '14 at 11:19
  • @nimrod, i have tried this, it is working fine, i am not seeing any issue? can you tell me , what you are looking for? – Arindam Nayak Sep 30 '14 at 11:20
  • 1
    @MatthewWatson nice comment, pointing to a document with like 1000 pages, very helpful^^ – DerApe Sep 30 '14 at 11:21
  • Note that `(int)theFloat` does the trick, although your question remains. – Patrick Hofman Sep 30 '14 at 11:22
  • @derape Did you read any of it? The section about rounding is right near the top. It's a famous article (among programmers) and programmers *should* be aware of it. – Matthew Watson Sep 30 '14 at 11:22
  • 1
    @MatthewWatson I did not, my point was just that sending someone to a document with that size without and clue what to look for is in general not very helpful (even if the thing he is looking for is in the very beginning) At least you could state where to look for... – DerApe Sep 30 '14 at 11:23
  • Related, possible duplicate of http://stackoverflow.com/questions/14325214/incorrect-rounding-of-float-when-using-tostringf1. – Patrick Hofman Sep 30 '14 at 11:24
  • @derape I disagree, and if you bother to read even the introduction you would surely see that it is very useful. – Matthew Watson Sep 30 '14 at 11:24
  • 1
    @MatthewWatson Also a little summary of what he will find when he follows that link would be nice, link could be not working in the future because of different reasons, so anyone who reads this would be helped if the answer is here on stack overflow instead of anywhere else. – Paul Weiland Sep 30 '14 at 11:25
  • 2
    @MeAndSomeRandoms That sort of thing is normally reserved for answers, not comments. – Matthew Watson Sep 30 '14 at 11:26
  • 1
    @MatthewWatson I'm not doubting the usefulness of the document and it's content, just your comment. He just don't know what to look for. Also the link might break in the future... – DerApe Sep 30 '14 at 11:26
  • @MatthewWatson Necessary for answers, true. But would not hurt for comments, would it ? – Paul Weiland Sep 30 '14 at 11:28
  • @MeAndSomeRandoms I don't really think that attempting to explain the details of floating point rounding errors in a comment will be very readable. – Matthew Watson Sep 30 '14 at 11:29
  • 2
    It didn't take me too long to find this gem in the documentation - "Squeezing infinitely many real numbers into a finite number of bits requires an approximate representation. ... given any fixed number of bits, most calculations with real numbers will produce quantities that cannot be exactly represented using that many bits. Therefore the result of a floating-point calculation must often be rounded in order to fit back into its finite representation. This rounding error is the characteristic feature of floating-point computation." – Enigmativity Sep 30 '14 at 11:33
  • "document with like 1000 pages" hyperbole! it is 72 pages ;) @Matthew Watson thanks for this link! – elimad Sep 30 '14 at 11:44

1 Answers1

13

From the documentation for float:

  • The range of a float is -3.4 × 10^38 to +3.4 × 10^38
  • The precision of a float is 7 digits.

Your number, 12345678, at 8 digits long exceeds the precision, so it is by default being rounded to 7 significant digits, which yields 12345680. (Note the by default.)

However, despite what that Microsoft article says about the precision of a float, in reality it holds up to 9 digits of precision.

The Microsoft documentation for Single.ToString() states:

By default, the return value only contains 7 digits of precision although a maximum of 9 digits is maintained internally.

It then goes on to say:

If you require more precision, specify format with the "G9" format specification, which always returns 9 digits of precision, or "R", which returns 7 digits if the number can be represented with that precision or 9 digits if the number can only be represented with maximum precision.

Armed with this information, we can write this code:

Console.WriteLine(12345678f.ToString("G9"));

Which does indeed print 12345678.

What I can't explain is why Microsoft state that a float has 7 digits of precision, and then goes on to let us use 9 digits...

However, note that not all 8 (or 9) decimal digit integers will have an exact representation in a float, as the following code demonstrates (the last digit differs):

Console.WriteLine(16777217f.ToString("R")); // Prints 16777216
Matthew Watson
  • 104,400
  • 10
  • 158
  • 276
  • According to this answer, the float number value should have been cut. The float value itself is OK (you can see it in the debugger). The problem is in the "ToString" method... – Nimrod Sep 30 '14 at 13:08
  • Also, as stated by @PatrickHofman, casting the float to int and printing it would do the job, which also proves the float value is intact – Nimrod Sep 30 '14 at 13:09
  • I don't quite understand how a float (which is also IEEE compatible) can hold more than 7 digits of precision in only 23 (OK, 24 if you like) bits of significand. I assume they use a double as intermediate. – Rudy Velthuis Sep 30 '14 at 14:23
  • @RudyVelthuis Nor me - but it's definitely not using a double since it really is stored in only 4 bytes. – Matthew Watson Sep 30 '14 at 14:56
  • @MatthewWatson: Hmmm... weird. OK, log10(2^24) is slightly more than exact 7, so it might work for 12345678 but, for instance, not for 12345679 (or some other two consecutive 8 digit values). And G9 is merely an output format. – Rudy Velthuis Sep 30 '14 at 15:04
  • @RudyVelthuis Indeed - I've discovered that `Console.WriteLine(16777217f.ToString("R"));` prints `16777216` - note the last digit! I'll add that to my answer. – Matthew Watson Sep 30 '14 at 15:20