in reply to sprintf and decimals

sprintf uses "Banker's rounding". ( Joost points out that this may be compiler-dependant. I'm using ActivePerl. )
4 < 5, so 2.674 rounds to 2.67
6 > 5, so 2.676 rounds to 2.68
5 = 5, and 7 is odd, so 2.675 rounds to 2.67
5 = 5, and 8 is even, so 2.685 rounds to 2.69

Banker's rounding (the even/odd bit) helps in the case of where one is adding a lot of rounded .5's

>perl -e "print 10.5 + 15.5 + 25.5" 51.5 (Unrounded) >perl -e "print 11 + 16 + 26" 53 (Conventional, delta = 1.5) >perl -e "print 10 + 16 + 26" 52 (Banker's, delta = 0.5)

Of course, it doesn't help in the case where one is adding lots of rounded .6's, but it doesn't hurt either.

>perl -e "print 10.6 + 15.6 + 25.6" 51.8 (Unrounded) >perl -e "print 11 + 16 + 26" 53 (Conventional, delta = 1.2) >perl -e "print 11 + 16 + 26" 53 (Banker's, delta = 1.2)

While it can produce a different result when the data is "well" distributed, it doesn't hurt there either.

>perl -e "print 0.0+0.1+0.2+0.3+0.4+0.5+0.6+0.7+0.8+0.9" 4.5 (Unrounded) >perl -e "print 0+0+0+0+0+1+1+1+1+1" 5 (Conventional, delta = 0.5) >perl -e "print 0+0+0+0+0+0+1+1+1+1" 4 (Banker's, delta = 0.5)

Update: Added background on banker's rounding.

Replies are listed 'Best First'.
Re^2: sprintf and decimals
by Joost (Canon) on Nov 15, 2006 at 18:54 UTC
      What about sprintf in POSIX? Is that one compiler-dependant too?
Re^2: sprintf and decimals (theory)
by tye (Sage) on Nov 15, 2006 at 23:16 UTC
    5 = 5, and 7 is odd, so 2.675 rounds to 2.67
    5 = 5, and 8 is even, so 2.685 rounds to 2.69

    Well, in theory. But floating point numbers can't actually represent 2.675 nor 2.685, so the rounding for these cases is actually determined by what numbers can be represented in floating point and are closest to those numbers:

    DB> x sprintf "%.20f %.2f", (2.675)x2 0 '2.67499999999999980000 2.67' DB> x sprintf "%.20f %.2f", (2.685)x2 0 '2.68500000000000010000 2.69' DB>

    which happens to agree with your assertion, but for the wrong reason. Change 6 to 4 to see a case that disagrees (at least on my system):

    DB> x sprintf "%.20f %.2f", (2.475)x2 0 '2.47500000000000010000 2.48' DB> x sprintf "%.20f %.2f", (2.485)x2 0 '2.48499999999999990000 2.48' DB>

    Finally, just to show how close floating point can get on the other side of one of these examples:

    DB> x pack "d", 2.675 0 "ffffff\cE\@" DB> x unpack "d", "gfffff\cE\@" 0 2.675 DB> x sprintf "%.20f %.2f", (2.675)x2 0 '2.67499999999999980000 2.67' DB> x sprintf "%.20f %.2f", (unpack "d", "gfffff\cE\@")x2 0 '2.67500000000000030000 2.68'

    And, this may give you some idea how many different values you might compute that Perl would report as just "2.675" but that can round differently:

    DB> x unpack "d", "qfffff\cE\@" 0 2.675 DB> x unpack "d", "rfffff\cE\@" 0 2.67500000000001

    - tye        

Re^2: sprintf and decimals
by nosbod (Scribe) on Nov 15, 2006 at 18:46 UTC
    thanks. So that'll be why this bit of code returns the following
    #!/usr/bin/perl $w=2.315; $x=2.3150141; $y=sprintf("%.2f", $w); $z=sprintf("%.2f", $x); print "$w rounded is $y\n"; print "$x rounded is $z\n";
    2.315 rounded is 2.31
    2.3150141 rounded is 2.32

      oh! I didn't know it looks at more than the next digit.

      To continue the example in my previous post, that means
      5.1 > 5, so 2.6751 rounds to 2.68