#### Do you have a question? Post it now! No Registration Necessary. Now with pictures!

**posted on**

- Bart Van der Donck

May 13, 2011, 11:19 am

I have to deal with a rounding issue which basically goes back to high-

precision calculations. The issue is that the outcome is already known

and should match.

----------------------------------------------------------------

Code:

----------------------------------------------------------------

#!/usr/bin/perl

use strict;

use warnings;

my $TOTEXC = 0;

my $TOTINC = 0;

my $TOTTAX = 0;

my @products = ( # product name, price, tax%, quantity

'Screen|506.21|21|4',

'Cables|425.50|21|5',

'Server|585.26|21|1',

'Battery|58.62|21|3',

'Keyb.|44.46|21|2',

'Mouse|15.25|21|6',

'Battery|66.03|21|5'

);

my $percent = 1.2958684232587526; # commission dealer

my $factor = 0.8567; # some fixed factor

print qq;

for (@products)

{

my ($prodname, $price, $tax, $quantity) = split /\|/, $_;

$price = ( $price + ($price

*** $factor / 100) ) ***$percent;

my $net = $price * $quantity;

my $brut = $net * ( 1 + ($tax / 100));

$TOTEXC+= $net;

$TOTINC+= $brut;

$TOTTAX+= $brut - $net;

$

___ = sprintf("%.2f", $___) for ($price, $net, $brut);

print qq;

}

$

___ = sprintf("%.2f", $___) for ($TOTEXC, $TOTINC, $TOTTAX);

print qq{\nNet: $TOTEXC\nTax: $TOTTAX\nBrut: $TOTINC\n};

----------------------------------------------------------------

Output:

----------------------------------------------------------------

PROD PRICE QUANT NET TAX BRUT

Screen 661.60 4 2646.41 21% 3202.15

Cables 556.12 5 2780.58 21% 3364.50

Server 764.92 1 764.92 21% 925.55

Battery 76.61 3 229.84 21% 278.11

Keyb. 58.11 2 116.22 21% 140.62

Mouse 19.93 6 119.59 21% 144.70

Battery 86.30 5 431.50 21% 522.11

Net: 7089.05

Tax: 1488.70

Brut: 8577.74

----------------------------------------------------------------

Questions:

----------------------------------------------------------------

- Is there any way to perform the calculations at extreme

high precision, so that all caculated alphanumerics match ?

- I have a known outcome (let's say eg. 8577.77) which was

done already in javascript by another application; is there

a way to guarantee that both outcomes are identical ?

- I have the feeling that this is maybe a common situation,

are there 'classical' approaches for this kind of things ?

- If I want to manually check the order, the calculations

must match (e.g. 'Net'+'Tax' must equal to 'Brut')

Thank you,

--

Bart

## Re: Rounding issue with defined outcome

Obviously you are doing something with money. In that case you usually

do NOT need extreme high precision because there are defined rules how

and when to round to how many digits.

It might be that you need to round to 2 (or 4) digits behind the decimal

point after each step.

So I can only give the advice to really look into the law how to add the

tax correctly and to find out what the contract with the commission

dealer really says.

Wolf

## Re: Rounding issue with defined outcome

Add this, and look carefully at the output it makes:

printf "%30.20f\n", $percent;

So things are wrong even before you start making any calculations...

Yes it is, and it is not restricted to Perl.

This problem applies to how numbers are stored in computers.

perldoc perlnumber

See also: What Every Computer Scientist Should Know About Floating-Point

Arithmetic

http://download.oracle.com/docs/cd/E19957-01/806-3568/ncg_goldberg.html

--

Tad McClellan

email: perl -le "print scalar reverse qq/moc.liamg0cm.j.dat/"

The above message is a Usenet post.

I don't recall having given anyone permission to use it on a Web site.

## Re: Rounding issue with defined outcome

Actually it doesn't. It goes back to understanding how calculations

work, e.g. basic computer numeric as well as basic rules for accounting.

Then you should follow the same processes and rules that resulted in the

already known outcome.

[...]

Yes, it is called symbolic calculations and there are more or less

expensive computer programs out there that do those. But that doesn't

help you one bit, because ...

... the already known outcome was not computed using extreme high

precision, either.

Yes. Use the same rules as have been used in the other application.

Yes. It is the misguided believe that higher precision equals better

results. THAT IS NOT THE CASE.

You should do your computations using the same method that was used in

the other application using the same precision and the same mathematical

operators with the same behaviour and the same algorithm.

In your case you should specifically check with accountants about how

sums are computed. Trivial example: if the commision is computed for

each individual transaction, listed in e.g. Euro and Cent, and then

added, then you will usually get a different result compared to adding

all transactions and then computing the commision on the total sales.

Yes, see Introduction into Basic Numerics for Computer Scientists

Then you need to calculate them the same way as they were calculated in

the other application..

jue

## Re: Rounding issue with defined outcome

[snip code]

convert to cents I think.

Then when ready to output, convert back to dollars (do not use sprintf

"%.2f").

Instead, use the substitution operator:

# Convert to pennies from dollars.

# $price is now an integer

$price =~ s/\.(?=3D\d\d$)// or die "Cannot expand $price to integer.

$!";

# convert from pennies to dollars, (insert decimal point), for

output.

s/(\d\d)$/.$1/ for $price, $net, $brut;

Hi Bart,

FWIW, I believe my code (below) exhibits Pennsylvania's sales tax.

#!/usr/bin/perl

use strict;

use warnings;

use 5.012;

use POSIX qw/ ceil /;

for my $cents (1 .. 111) {

my $tax = ceil($cents * .06);

my $scale = $cents % 100;

$tax-- if $scale >=3D 1 && $scale <=3D 10;

say "$cents $tax";

}

Note that this state (perhaps others too) take the 'ceil(ing)' of the

cents. (Use cents because I am now dealing with integers). Then it is

necessary to convert back to dollars for output purposes.

Chris

## Re: Rounding issue with defined outcome

Why not? Can you show a case where it produces the wrong result?

OTOH:

This will not work for amount <= 99 cents.

my $tax = ceil($cents * 6 / 100);

otherwise you multiply with

0.059999999999999997779553950749686919152736663818359375 which may or

may not make a difference.

hp

## Re: Rounding issue with defined outcome

No, I was assuming that by avoiding the sprintf I could avoid a

rounding error.

Good catch. I think you meant it will not work for <=3D 9 cents. No

match and no placement of the decimal point. I should have been more

careful in uncharted (for me) territory. s/(\d\d?)$/.$1/ would be the

correct answer I believe?

0 for 3. Not very good.

## Re: Rounding issue with defined outcome

If you use sprintf("%.2f", $x/100) there will indeed be a rounding error

in the computation of $x/100 and possibly another one in rounding two

digits. Those rounding errors will be somewhere in the magnitude of

$x/100/2

******53 (assuming IEEE double precision floating point arithmetic),

and as long as that error is less than 0.005, it won't affect the

result. So $x/100/2

******53 <= 0.005, therefore $x <= 0.005

*** 2***

***53 ***100

or $x <= 4.5E15. Lets be conservative and say you're safe if $x is less

than 1E14.

Actually I meant <= 99 cents, but I realize that there are locales where

".99" (instead of "0.99") is correct. So if your bean counter doesn't

throw a hissy fit over the missing zero, that's ok. For <= 9 cents it's

always incorrect.

No that converts 9 to ".9" instead of ".09".

This would work:

for my $cents (9, 99, 999, 999

___999___999_999) {

my $dollars = sprintf("%03.0f", $cents);

$dollars =~ s/(\d\d)$/.$1/;

print "$cents -> $dollars\n";

}

hp

## Re: Rounding issue with defined outcome

wrote:

[snip code]

There could be a typo in your numbers/code.

If the Brut total were to be 8577.77 , the Net and Tax totals

would be significantly alterred. So, I asume you are not giving

the exact input/output from the code where you state the known

outcome of the Brut total of 8577.77

The reason I know this is that :

1) Net/Tax/Brut intermediate values are all interrelated.

2) Applying arithmetic significance of measured values do not

alter the results.

ie.. percent ~ 1.2958685 does not alter the outcome (a measured value)

factor ~ 0.8567 is the least significant (also a measured value)

3) Applying sprintf "%.4f" to $brut intermediate raises the Brut total

at the expence of Tax and Net. Even then, it only raises Brut to 8577.75

4) Rounding percent and factor to make Brut fit in the 8577.77 range,

significantly alters Tax and Net.

I think its probable there is some other non-mathmatical error going on here.

-sln

[snip code]

There could be a typo in your numbers/code.

If the Brut total were to be 8577.77 , the Net and Tax totals

would be significantly alterred. So, I asume you are not giving

the exact input/output from the code where you state the known

outcome of the Brut total of 8577.77

The reason I know this is that :

1) Net/Tax/Brut intermediate values are all interrelated.

2) Applying arithmetic significance of measured values do not

alter the results.

ie.. percent ~ 1.2958685 does not alter the outcome (a measured value)

factor ~ 0.8567 is the least significant (also a measured value)

3) Applying sprintf "%.4f" to $brut intermediate raises the Brut total

at the expence of Tax and Net. Even then, it only raises Brut to 8577.75

4) Rounding percent and factor to make Brut fit in the 8577.77 range,

significantly alters Tax and Net.

I think its probable there is some other non-mathmatical error going on here.

-sln

#### Site Timeline

- » FAQ 8.37 How do I find out if I'm running interactively or not?
- — Next thread in » PERL Discussions

- » FAQ 1.5 What was Ponie?
- — Previous thread in » PERL Discussions

- » s suffix question
- — Newest thread in » PERL Discussions

- » Dell Battery Slice LED codes
- — The site's Newest Thread. Posted in » Laptop Computers Forum