3

I'm trying to display floats to just one decimal point. I'm getting unexpected results as follows:

Code:

float a = 1.25;
float b = 1.35;

NSLog(@"1.25 -> %.1f\n1.35 -> %.1f",a,b);

Output:

1.25 -> 1.2

1.35 -> 1.4

Expected output, either:

1.25 -> 1.3

1.35 -> 1.4

or:

1.25 -> 1.2

1.35 -> 1.3

Is this simply due to the internal conversion between binary and decimal? If so, how do I get the expected behaviour?

I'm using Xcode 4.6.

edit: Okay, thanks to TonyK and H2CO3 it's due to the binary representation of decimals.

float a = 1.25;
float b = 1.35;
NSLog(@"1.25 -> %.30f\n1.35 -> %.30f",a,b);

1.25 -> 1.250000000000000000000000000000

1.35 -> 1.350000000000000088817841970013

Lots of good info, but as far as I can see no one has approached the second question: How do I get the expected behaviour?

Rounding numbers in Objective-C is a quite different question.

Community
  • 1
  • 1
putridp
  • 55
  • 5
  • 1
    This relates to all the C languages, not just Objective C. What you get depends on the system, 1.25 could be printed as 1.2 or 1.3. – teppic Mar 31 '13 at 10:31

2 Answers2

5

1.35 is 27/20, which in binary is

1.01 0110 0110 0110 0110 0110 0110....

A float has a 23-bit mantissa on most systems (not counting the implied leading 1.), so this gets rounded up to

1.01 0110 0110 0110 0110 0110 1

(because 0110 is unambiguously greater than half of 1000). So it's strictly greater than 1.35 by the time printf sees it. Hence 1.4.

As for 1.25, this is exactly representable in binary as

1.01

So printf sees its exact value. But how should it round 1.25? We were taught in school to round 5 up to 10. But most modern systems use a default rounding mode called "round to even" at the hardware level, because it lessens the effect of cumulative rounding errors. This means that when a number is exactly between the two nearest candidates for rounding, it gets rounded to the even candidate.

So it seems that print is using "round to even" for decimal output! I checked this hypothesis at this ideone link, and indeed 1.75 gets rounded up, to 1.8. This is a surprise to me, but not a huge one.

TonyK
  • 16,761
  • 4
  • 37
  • 72
  • Thanks for making an effort digging through the full binary representation. +1. –  Mar 31 '13 at 11:02
2

That's because floating-point numbers aren't exact. %.1f prints the number rounded to one decimal place, it seems, however, that 1.35 can't be exactly represented as 1.2500000, instead it's a slightly smaller number that can be.

Read about this behavior here: What every computer scientist should know about floating-point numbers.

  • 3
    1.25 = 5/4 can be represented exactly, because 4 is a power of 2. It's 1.35 = 27/20 that can't be represented exactly. – TonyK Mar 31 '13 at 10:03
  • 1
    @TonyK Right, but then why is it truncated instead of rounded? –  Mar 31 '13 at 10:04
  • Okay, thank you TonyK and H2CO3. You might interested in my edit which shows the behaviour of %.30f as demonstration. What's the usual way of working around this to get consistent behaviour? – putridp Mar 31 '13 at 10:28