http://www.perlmonks.org?node_id=804204

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

Was looking out for the rounding convention used by perl sprintf in-built function. I was thinking that it does a normal rounding (ROUND_HALF_UP - in Java's rounding mode convention - http://www.j2ee.me/javase/6/docs/api/java/math/RoundingMode.html), but the digging up further proved to be wrong. >> /usr/local/bin/perl5.10.1 -e 'print(sprintf("%.2f", shift @ARGV)."\n");' 0.335 0.34 >> /usr/local/bin/perl5.10.1 -e 'print(sprintf("%.2f", shift @ARGV)."\n");' 1.335 1.33

Replies are listed 'Best First'.
Re: sprintf rounding convention
by Joost (Canon) on Oct 30, 2009 at 19:22 UTC
    "Normal rounding" is a misleading term. As far as I know (and I can't be bothered to look it up) sprintf rounding uses whatever rounding libc uses, which tends but isn't guaranteed to be "banker's rounding". Banker's rounding, or HALF_EVEN in Java parlance*, is useful because it tends to lead to less distortion over large sums while still being predictable.

    * HALF_EVEN is also the strategy Java uses for float and double maths.

      That's incorrect. %f and other floating point specifiers always round to even. From F0convert:
      nv += 0.5; uv = (UV)nv; if (uv & 1 && uv == nv) uv--; /* Round to even */

      (There are paths I didn't take the time to understand, so maybe it doesn't always get to this function.)

        Hmm... Then I think the sprintf documentation is incorrect, or possibly just confusing: (emphasis mine)
        Perl does its own "sprintf" formatting--it emulates the C function "sprintf", but it doesn't use it (except for floating- point numbers, and even then only the standard modifiers are allowed). As a result, any non-standard extensions in your local "sprintf" are not available from Perl.
        Now I'm confused:

        $ perl -v This is perl, v5.8.8 built for x86_64-linux-gnu-thread-multi $ perl -e 'printf "%.2f\n%.2f\n", 0.335, 1.335;' 0.34 1.33

        and

        C:\>perl -v This is perl, v5.8.9 built for MSWin32-x86-multi-thread (with 9 registered patches, see perl -V for more detail) C:\>perl -e "printf qq{%.2f\n%.2f\n}, 0.335, 1.335;" 0.34 1.34

        Am I missing something (as usual)?

        Update:

        Following ikegami's suggestion, I tried increasing the precision to 40 digits, and got:

        $ perl -v This is perl, v5.8.8 built for x86_64-linux-gnu-thread-multi $ perl -e 'printf "%.40f\n%.40f\n", 0.335, 1.335;' 0.3350000000000000199840144432528177276254 1.3349999999999999644728632119949907064438 C:\>perl -v This is perl, v5.8.9 built for MSWin32-x86-multi-thread (with 9 registered patches, see perl -V for more detail) C:\>perl -e "printf qq{%.40f\n%.40f\n}, 0.335, 1.335;" 0.3350000000000000200000000000000000000000 1.3350000000000000000000000000000000000000

        It looks like this variation is just an artifact of 64-bit vs. 32-bit, emphasizing how you should never rely on precise results from rounding.

Re: sprintf rounding convention
by Anonymous Monk on Oct 30, 2009 at 19:23 UTC