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

Hi fellow Monks!
So, I have a script that produces several fractions, by using the sprinf function, like this:
$rounded_zero = 100*sprintf("%.2f", $fraction_zero); $rounded_single = 100*sprintf("%.2f", $fraction_single); $rounded_two = 100*sprintf("%.2f", $fraction_two); $rounded_multi = 100*sprintf("%.2f", $fraction_multi);

My problem is that in some cases, the total sum is either 99 or 100, because of the rounding of the numbers. Is there a way I can calculate this and make sure it adds up to 100?

Replies are listed 'Best First'.
Re: How to calculate the sum of columns to be equal to 100?
by Athanasius (Archbishop) on Aug 15, 2013 at 12:14 UTC

    Laurent_R is correct, but you can get close(r) to what you want with a better rounding function. Consider:

    #! perl use strict; use warnings; my @fractions = (3 / 11, 2 / 11, 1 / 11, 5 / 11); my @rounded_old = map { round_old($_) } @fractions; my @rounded_new = map { round_new($_) } @fractions; my $sum_old = 0; $sum_old += $_ for @rounded_old; my $sum_new = 0; $sum_new += $_ for @rounded_new; printf "\nOld sum: %.2f new sum: %.2f\n", $sum_old, $sum_new; sub round_old { return 100 * sprintf("%.2f", shift); } sub round_new { return sprintf("%.2f", int((1e5 * shift) + 0.5) / 1e3); }

    Output:

    22:10 >perl 688_SoPW.pl Old sum: 99.00 new sum: 99.99 22:10 >

    Hope that helps,

    Athanasius <°(((><contra mundum Iustus alius egestas vitae, eros Piratica,

Re: How to calculate the sum of columns to be equal to 100?
by Laurent_R (Canon) on Aug 15, 2013 at 12:03 UTC

    Please tell us what you have in $fraction_zero, $fraction_single, etc.

    But in general, the sum of rounded amounts cannot be guaranteed to be equal to the rounded total of the same not-rounded amounts. This is not a Perl issue, but simple arithmetics.

Re: How to calculate the sum of columns to be equal to 100?
by ambrus (Abbot) on Aug 15, 2013 at 12:34 UTC

      Yes, fudging the data is one solution.

      The company for which I work as an independent consultant spent a few years back several hundred thousand euros trying to get their invoices "look right" (about 20 million invoices per month). After a lot of thinking, simulations on Excel, consultations with accountants, auditors and tax advisers, prototyping, etc., it was finally decided that all the invoice individual lines and subtotals would have 5 decimal digits, and only the invoice total (and VAT) would be rounded to the nearest cent. There may still be some rounding differences, but, at least, nobody really cares about a difference of 0.00001 euro.

        I have also seen invoices in which the last line before the invoice total is a "rounding compensation", something like -0.01.
        لսႽ† ᥲᥒ⚪⟊Ⴙᘓᖇ Ꮅᘓᖇ⎱ Ⴙᥲ𝇋ƙᘓᖇ
Re: How to calculate the sum of columns to be equal to 100?
by mtmcc (Hermit) on Aug 15, 2013 at 11:54 UTC
    Can you give an example of the data your using, what result you get, and what result you expect to get?
Re: How to calculate the sum of columns to be equal to 100?
by locked_user sundialsvc4 (Abbot) on Aug 15, 2013 at 12:22 UTC

    In any programming language (much to the vexation of accountants ... who, I cordially submit, get whatever they deserve), dollars-and-cents when treated as floating-point numbers will not always add-up “to the penny.”   In fact they can be off by several cents, depending on exactly how the information was processed.

    One floating-point technique that is sometimes used is called banker’s rounding,” which rounds even-ending numbers one way, odd-ending numbers the other.   (I don’t rightly recall if Perl implements it.)   Another technique, used in (for instance) Microsoft Access for its CURRENCY data-type, is to use scaled integers.   Internally, the number is multiplied by 10000 to give a fixed four digits of precision. But, even then, it might not add-up perfectly.   It’s the math.   Accountants, you see, inadvertantly “lose the money ahead of time” by rounding to (say) two decimal-places first.   Their desk-calculators have a special mode to do that, which will produce different answers if instead set to ordinary floating-point mode.   (Politically-connected accountants have a different mode yet, which allows the answer to be “anything you want!”)   ;-)

    Reproduce a similar issue in your accountant’s precious Microsoft Excel Spreadsheet, which you certainly can, and (s)he’ll be quiet(er).

    ac - count - ant (n):
    A person who will cheerfully spend a dollar looking for a penny, then bill you for it.

    au - dit -or (n):
    A highly-paid professional who will certify that both answers are equally wrong.

      One floating-point technique that is sometimes used is called banker’s rounding,” which rounds even-ending numbers one way, odd-ending numbers the other. (I don’t rightly recall if Perl implements it.)

      Yes, Perl implements it, because it uses C libraries which implement the rounding recommended by IEEE.

      The idea is the following. Suppose, to take a simple case, that you want to round to the unit numbers which have only one decimal digit. Any number where the decimal digit is less than 5 will be rounded down and any number where the decimal digit is larger than 5 is rounded up. But what do you do if the decimal part is exactly 5? Say, for example, how do you round 3.5? The most usual method rounds such a number up. But bankers claim that this introduces a bias towards rounding up: out of ten possible decimal digits, one will not be rounded (0), 4 will be rounded down (1, 2, 3 and 4) and 5 will be rounded up (5 to 9). This can make a difference if you add a long series of numbers. So they decided that the rounding of the 5 decimal digit will be rounded up or down, depending on whether the previous digit is odd or even.

      This is what you can see in the somewhat strange output of a Perl one-liner below:

      $ perl -e 'print "raw\t\trounded\n"; printf "%f\t%.0f\n", $_+0.5, $_+ +0.5 for 0..10; ' raw rounded 0.500000 0 1.500000 2 2.500000 2 3.500000 4 4.500000 4 5.500000 6 6.500000 6 7.500000 8 8.500000 8 9.500000 10 10.500000 10

      I actually once had to write a special rounding module just because my client considered the above to be simply wrong and wanted 2.5 to be rounded to 3.

        Thanks Laurent_R, this is useful information I hadn’t come across before. Is it documented anywhere? I don’t see anything in sprintf or the Camel Book, and the latest C Standard doesn’t seem to cover it either?

        I actually once had to write a special rounding module just because my client considered the above to be simply wrong and wanted 2.5 to be rounded to 3.

        Simply adding a minimum field width to the format seems to do the trick:

        1:03 >perl -wE "printf qq[%9f\t%.0f\t%2.0f\n], ($_ + 0.5) x 3 for 0 . +. 10;" 0.500000 0 1 1.500000 2 2 2.500000 2 3 3.500000 4 4 4.500000 4 5 5.500000 6 6 6.500000 6 7 7.500000 8 8 8.500000 8 9 9.500000 10 10 10.500000 10 11 1:03 >perl -v This is perl 5, version 18, subversion 0 (v5.18.0) built for MSWin32-x +86-multi-thread-64int

        It should perhaps also be pointed out that where the internal representation of a floating point number is inexact, all bets are off:

        1:29 >perl -wE "printf qq[%21.18f\t%.1f\t%4.1f\n], ($_ + 0.05) x 3 fo +r 0 .. 10;" 0.050000000000000003 0.1 0.1 1.050000000000000000 1.1 1.1 2.049999999999999800 2.0 2.0 3.049999999999999800 3.0 3.0 4.049999999999999800 4.0 4.0 5.049999999999999800 5.0 5.0 6.049999999999999800 6.0 6.0 7.049999999999999800 7.0 7.0 8.050000000000000700 8.1 8.1 9.050000000000000700 9.1 9.1 10.050000000000001000 10.1 10.1 1:29 >

        Cheers,

        Athanasius <°(((><contra mundum Iustus alius egestas vitae, eros Piratica,