nosbod has asked for the wisdom of the Perl Monks concerning the following question:

hi, I may be going mad here but shouldn't the output from the below be
2.6745508 rounded is 2.68
NOT
2.6745508 rounded is 2.67
?
#!/usr/bin/perl $x=2.6745508; $y=sprintf("%.2f", $x); print "$x rounded is $y";
cheers

Replies are listed 'Best First'.
Re: sprintf and decimals
by ikegami (Patriarch) on Nov 15, 2006 at 18:23 UTC

    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.

        What about sprintf in POSIX? Is that one compiler-dependant too?
      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        

      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

Re: sprintf and decimals
by swampyankee (Parson) on Nov 15, 2006 at 18:46 UTC

    No. Perl and ikegami are correct.

    If your logic is what I think it is, you're doing something like this:

    2.6745508 -> 2.676 2.676 -> 2.68

    This is wrong: rounding is not done in stages. In this case, the entire expansion to the right of the hundredths place is processed together. In other words, for your case, you round based on 45508.

    emc

    At that time [1909] the chief engineer was almost always the chief test pilot as well. That had the fortunate result of eliminating poor engineering early in aviation.

    —Igor Sikorsky, reported in AOPA Pilot magazine February 2003.
Re: sprintf and decimals
by Joost (Canon) on Nov 15, 2006 at 18:30 UTC
Re: sprintf and decimals
by fenLisesi (Priest) on Nov 15, 2006 at 18:35 UTC
    Is there any environment where you get 2.68? gcc should give you 2.67, too.

      Is there any environment where you get 2.68?

      If there is, it's wrong.

      gcc should give you 2.67, too.

      Yes, it should. So should C, C++, COBOL, Pascal, Fortran (it does), Python, Ruby, Haskell, Erlang, VB, C#, etc.…. The rounding rules are not just custom; there are actually international standards.

      emc

      At that time [1909] the chief engineer was almost always the chief test pilot as well. That had the fortunate result of eliminating poor engineering early in aviation.

      —Igor Sikorsky, reported in AOPA Pilot magazine February 2003.