# Decimal arithmetic, with example code

Paul Boddie paul at boddie.net
Tue Oct 1 18:32:35 CEST 2002

```"Chris Gonnerman" <chris.gonnerman at newcenturycomputers.net> wrote in message news:<mailman.1033438201.27954.python-list at python.org>...
> """fp-test.py -- fixedpoint versus float comparison"""

It's interesting to compare the results from FixedPoint with
ScaledDecimal and with FixedPoint objects that have increased
precision. Many issues in the supplied program can explain the
apparent difference between rounded floating point results and the
FixedPoint results. Here are some example results from an extended
version of the original program:

PCT    AMT    Fixed  Fixed  Fixed  Scaled Scaled Float
2dp    4dp    -> 2dp 2dp    4dp
0.01   0.50   0.00   0.0050 0.00   0.01   0.0050 0.01
0.02   0.25   0.00   0.0050 0.00   0.01   0.0050 0.01
0.05   0.10   0.00   0.0050 0.00   0.01   0.0050 0.01
0.05   0.50   0.02   0.0250 0.02   0.03   0.0250 0.03
0.05   0.70   0.04   0.0350 0.04   0.04   0.0350 0.03
0.05   0.90   0.04   0.0450 0.04   0.05   0.0450 0.05
0.06   0.75   0.04   0.0450 0.04   0.05   0.0450 0.05
0.09   0.50   0.04   0.0450 0.04   0.05   0.0450 0.05
0.10   0.05   0.00   0.0050 0.00   0.01   0.0050 0.01
0.10   0.25   0.02   0.0250 0.02   0.03   0.0250 0.03
0.10   0.35   0.04   0.0350 0.04   0.04   0.0350 0.03
0.10   0.45   0.04   0.0450 0.04   0.05   0.0450 0.05
0.10   0.65   0.06   0.0650 0.06   0.07   0.0650 0.07
0.10   0.85   0.08   0.0850 0.08   0.09   0.0850 0.09

When the floating point result is only a "wafer thin mint" below 0.35
(on this Pentium III laptop running Windows 2000), the rounded,
truncated result is given as 0.3. Such conditions could be seen to be
a flaw when attempting to highlight problems with the FixedPoint
implementation. Meanwhile, ScaledDecimal's reliance on the long
integer arithmetic in Python, as well as the automatic extension of
precision in certain operations, do seem to help it produce the
results you were *probably* expecting.

"""fp-test.py -- fixedpoint versus float comparison"""

# You need the fixedpoint.py module installed
#    http://fixedpoint.sourceforge.net
# and the ScaledDecimal.py module installed

from fixedpoint import FixedPoint
from ScaledDecimal import ScaledDecimal

print "PCT    AMT    Fixed  Fixed  Fixed  Scaled Scaled Float"
print "              2dp    4dp    -> 2dp 2dp    4dp"

# I'm checking percentages, from 1% to 10% inclusive,
# multiplied by "money" amounts from 0.01 (one cent)
# to 1.00 (a buck).

# Given appropriate precision, this problem applies
# to other monetary systems than just USA.

for percent in range(1,11):

fixed_pct = FixedPoint(percent)
fixed_pct /= 100
fixed_pct_ext = FixedPoint(percent, 4)
fixed_pct_ext /= 100
# Think of it as 1..11 * (10 ** -2)
scaled_pct = ScaledDecimal(percent, -2)
float_pct = percent / 100.0

for amount in range(1,101):

fixed_amt = FixedPoint(amount)
fixed_amt /= 100
fixed_amt_ext = FixedPoint(amount, 4)
fixed_amt_ext /= 100
scaled_amt = ScaledDecimal(amount, -2)
float_amt = amount / 100.0

##############################################################
# Now for the crux of the matter:

fixed_res = fixed_amt * fixed_pct  # rounding is implicit here
fixed_res_ext = fixed_amt_ext * fixed_pct_ext
fixed_res_ext_rounded = fixed_res_ext.copy()
fixed_res_ext_rounded.set_precision(2)
scaled_res = scaled_amt * scaled_pct  # precision is increased
float_res = round(float_amt * float_pct, 2)

##############################################################
# That's it, let's see if they match:

if str(fixed_res) != ("%0.2f" % float_res) or \
str(scaled_res.round(-2)) != ("%0.2f" % float_res):

print "0.%02d   0.%02d   %-6s %-6s %-6s %-6s %-6s %-6.2f"
% \
(percent, amount, fixed_res, fixed_res_ext,
fixed_res_ext_rounded, scaled_res.round(-2),
scaled_res.round(-4), float_res)

# end of file.

```