Decimal arithmetic, with example code
Bengt Richter
bokr at oz.net
Tue Oct 1 17:03:53 EDT 2002
On Mon, 30 Sep 2002 21:12:00 -0500, "Chris Gonnerman" <chris.gonnerman at newcenturycomputers.net> wrote:
[... test program whose modified version appears below ...]
Hi Chris,
This is unproved, but maybe with the help of timbot et al we can prove
the range over which is is safe and useful. The test run doesn't prove
anything, but I thought it interesting. Just run it as you would your version.
----< fptester.py >---------------
# fptester.py -- adding experimental evenround test to fptest.py - bokr
#
# On Mon, 30 Sep 2002 21:12:00 -0500,
# "Chris Gonnerman" <chris.gonnerman at newcenturycomputers.net> wrote:
#
# >This is a multi-part message in MIME format.
# >
# >------=_NextPart_000_000B_01C268C6.04954560
# >Content-Type: text/plain;
# > charset="Windows-1252"
# >Content-Transfer-Encoding: 7bit
# >
# Ok, here it is. I have written some Python code which
# displays the problems with float (floating binary)
# arithmetic applied to monetary amounts. Run this, and
# many the places where the simple percentage times
# amount calculation in floating binary falls down will
# become obvious.
#
# You need the fixedpoint.py module installed to run this.
#
# http://fixedpoint.sourceforge.net
#
# (code attached)
#
# Chris Gonnerman -- chris.gonnerman at newcenturycomputers.net
# http://newcenturycomputers.net
# ___________________________________________________________________________
#
# Chris,
# The evenround function I defined below seems (!) to do ok for your test,
# and then some (I extended it to run through with smaller and smaller
# percentages using increasing numbers of decimal fraction digits)
#
# Warning! This is as yet unproved. I just had the idea in response to the
# discussion. So don't go putting it in production software! You have been advised ;-)
#
# I modified your code a fair amount to incorporate and test my rounding function,
# as you can see. It would be interesting to prove what the range of validity
# really is.
#
# Anyway, run this just as you would your version.
# BTW, straight text is nicer to post ;-)
#
# Regards,
# Bengt Richter
# ___________________________________________________________________________
"""fptester.py -- fixedpoint versus float versus evenrounded float comparison"""
# You need the fixedpoint.py module installed
# http://fixedpoint.sourceforge.net
from fixedpoint import FixedPoint
FMTCOLS = '%-14s '*5 + '%s'
TITLES = "PCT AMT Fixed Float EFloat EFloat==Fixed".split()
print FMTCOLS % tuple(TITLES)
print FMTCOLS % tuple(map(lambda x:'-'*len(x), TITLES))
# I'm checking percentages, from 1% to 10% inclusive,
# multiplied by "money" amounts from 0.01 (one cent)
# to 1.00 (a buck).
# (I added an outer loop to scale the percentages down by factors of 10**x -- Bengt)
# Given appropriate precision, this problem applies
# to other monetary systems than just USA.
####################
# Here is my evenround function, apparently letting you use floating point as-is
# over a large range until it's time to round and print with %f format.
# -- Bengt Richter (NO WARRANTIES! Experimental software!)
#
EPSILON = 1.0e-14
BIAS = 1.0 + EPSILON
def evenround(f, nfdec):
"""
Return floating value that will round to even trailing digit
with nfdec fractional digits in %.0<nfdec>f format.
"""
f, fsigned = abs(f), f
half = 0.5 * 10.**-nfdec
f *= BIAS
nh, rh = divmod(f, half)
f -= (rh<EPSILON and int(nh)%4==1)*half*BIAS
if fsigned<0: return -f
return f
#
####################
# Here I run the percentages through smaller-by-factors-of-ten ranges
for decimals in range(2,13):
ffmt = '%%.0%df' % decimals
for percent in range(1,11):
fixed_pct = FixedPoint(percent,decimals)
fixed_pct /= 10**decimals
float_pct = percent / 10.0**decimals
for amount in range(1,101):
fixed_amt = FixedPoint(amount, decimals)
fixed_amt /= 100
float_amt = amount / 100.0
##############################################################
# Now for the crux of the matter:
fixed_res = fixed_amt * fixed_pct # rounding is implicit here
float_res = round(float_amt * float_pct, decimals)
even_res = evenround(float_amt * float_pct, decimals)
##############################################################
# That's it, let's see if they match:
sfix, sfloat, seven = (
str(fixed_res), (ffmt % float_res), (ffmt % even_res)
)
if sfix != sfloat or sfix != seven:
print FMTCOLS % (
fixed_pct, fixed_amt, # for ease ;-)
sfix, sfloat, seven, ('No, BUMMER!', 'Yes')[sfix==seven]
)
# end of file.
----------------------------------
Results are :
[14:01] C:\pywk\fixedpt>python fptester.py
PCT AMT Fixed Float EFloat EFloat==Fixed
--- --- ----- ----- ------ -------------
0.01 0.50 0.00 0.01 0.00 Yes
0.02 0.25 0.00 0.01 0.00 Yes
0.05 0.10 0.00 0.01 0.00 Yes
0.05 0.50 0.02 0.03 0.02 Yes
0.05 0.70 0.04 0.03 0.04 Yes
0.05 0.90 0.04 0.05 0.04 Yes
0.06 0.75 0.04 0.05 0.04 Yes
0.09 0.50 0.04 0.05 0.04 Yes
0.10 0.05 0.00 0.01 0.00 Yes
0.10 0.25 0.02 0.03 0.02 Yes
0.10 0.35 0.04 0.03 0.04 Yes
0.10 0.45 0.04 0.05 0.04 Yes
0.10 0.65 0.06 0.07 0.06 Yes
0.10 0.85 0.08 0.09 0.08 Yes
0.001 0.500 0.000 0.001 0.000 Yes
0.002 0.250 0.000 0.001 0.000 Yes
0.005 0.100 0.000 0.001 0.000 Yes
0.005 0.500 0.002 0.003 0.002 Yes
0.005 0.700 0.004 0.003 0.004 Yes
0.005 0.900 0.004 0.005 0.004 Yes
0.006 0.750 0.004 0.005 0.004 Yes
0.009 0.500 0.004 0.005 0.004 Yes
0.010 0.050 0.000 0.001 0.000 Yes
0.010 0.250 0.002 0.003 0.002 Yes
0.010 0.350 0.004 0.003 0.004 Yes
0.010 0.450 0.004 0.005 0.004 Yes
0.010 0.650 0.006 0.007 0.006 Yes
0.010 0.850 0.008 0.009 0.008 Yes
0.0001 0.5000 0.0000 0.0001 0.0000 Yes
0.0002 0.2500 0.0000 0.0001 0.0000 Yes
0.0003 0.5000 0.0002 0.0001 0.0002 Yes
0.0005 0.1000 0.0000 0.0001 0.0000 Yes
0.0005 0.3000 0.0002 0.0001 0.0002 Yes
0.0005 0.5000 0.0002 0.0003 0.0002 Yes
0.0005 0.9000 0.0004 0.0005 0.0004 Yes
0.0006 0.2500 0.0002 0.0001 0.0002 Yes
0.0006 0.7500 0.0004 0.0005 0.0004 Yes
0.0009 0.5000 0.0004 0.0005 0.0004 Yes
0.0010 0.0500 0.0000 0.0001 0.0000 Yes
0.0010 0.1500 0.0002 0.0001 0.0002 Yes
0.0010 0.2500 0.0002 0.0003 0.0002 Yes
0.0010 0.4500 0.0004 0.0005 0.0004 Yes
0.0010 0.6500 0.0006 0.0007 0.0006 Yes
0.0010 0.8500 0.0008 0.0009 0.0008 Yes
0.00001 0.50000 0.00000 0.00001 0.00000 Yes
0.00002 0.25000 0.00000 0.00001 0.00000 Yes
0.00005 0.10000 0.00000 0.00001 0.00000 Yes
0.00005 0.50000 0.00002 0.00003 0.00002 Yes
0.00005 0.70000 0.00004 0.00003 0.00004 Yes
0.00005 0.90000 0.00004 0.00005 0.00004 Yes
0.00006 0.75000 0.00004 0.00005 0.00004 Yes
0.00007 0.50000 0.00004 0.00003 0.00004 Yes
0.00009 0.50000 0.00004 0.00005 0.00004 Yes
0.00010 0.05000 0.00000 0.00001 0.00000 Yes
0.00010 0.25000 0.00002 0.00003 0.00002 Yes
0.00010 0.35000 0.00004 0.00003 0.00004 Yes
0.00010 0.45000 0.00004 0.00005 0.00004 Yes
0.00010 0.65000 0.00006 0.00007 0.00006 Yes
0.00010 0.85000 0.00008 0.00009 0.00008 Yes
0.000001 0.500000 0.000000 0.000001 0.000000 Yes
0.000002 0.250000 0.000000 0.000001 0.000000 Yes
0.000005 0.100000 0.000000 0.000001 0.000000 Yes
0.000005 0.500000 0.000002 0.000003 0.000002 Yes
0.000005 0.900000 0.000004 0.000005 0.000004 Yes
0.000006 0.750000 0.000004 0.000005 0.000004 Yes
0.000009 0.500000 0.000004 0.000005 0.000004 Yes
0.000010 0.050000 0.000000 0.000001 0.000000 Yes
0.000010 0.250000 0.000002 0.000003 0.000002 Yes
0.000010 0.450000 0.000004 0.000005 0.000004 Yes
0.000010 0.650000 0.000006 0.000007 0.000006 Yes
0.000010 0.850000 0.000008 0.000009 0.000008 Yes
0.0000001 0.5000000 0.0000000 0.0000001 0.0000000 Yes
0.0000002 0.2500000 0.0000000 0.0000001 0.0000000 Yes
0.0000005 0.1000000 0.0000000 0.0000001 0.0000000 Yes
0.0000005 0.5000000 0.0000002 0.0000003 0.0000002 Yes
0.0000005 0.9000000 0.0000004 0.0000005 0.0000004 Yes
0.0000006 0.7500000 0.0000004 0.0000005 0.0000004 Yes
0.0000009 0.5000000 0.0000004 0.0000005 0.0000004 Yes
0.0000010 0.0500000 0.0000000 0.0000001 0.0000000 Yes
0.0000010 0.2500000 0.0000002 0.0000003 0.0000002 Yes
0.0000010 0.4500000 0.0000004 0.0000005 0.0000004 Yes
0.0000010 0.6500000 0.0000006 0.0000007 0.0000006 Yes
0.0000010 0.9500000 0.0000010 0.0000009 0.0000010 Yes
0.00000001 0.50000000 0.00000000 0.00000001 0.00000000 Yes
0.00000002 0.25000000 0.00000000 0.00000001 0.00000000 Yes
0.00000003 0.50000000 0.00000002 0.00000001 0.00000002 Yes
0.00000005 0.10000000 0.00000000 0.00000001 0.00000000 Yes
0.00000005 0.30000000 0.00000002 0.00000001 0.00000002 Yes
0.00000005 0.50000000 0.00000002 0.00000003 0.00000002 Yes
0.00000005 0.70000000 0.00000004 0.00000003 0.00000004 Yes
0.00000005 0.90000000 0.00000004 0.00000005 0.00000004 Yes
0.00000006 0.25000000 0.00000002 0.00000001 0.00000002 Yes
0.00000009 0.50000000 0.00000004 0.00000005 0.00000004 Yes
0.00000010 0.05000000 0.00000000 0.00000001 0.00000000 Yes
0.00000010 0.15000000 0.00000002 0.00000001 0.00000002 Yes
0.00000010 0.25000000 0.00000002 0.00000003 0.00000002 Yes
0.00000010 0.35000000 0.00000004 0.00000003 0.00000004 Yes
0.00000010 0.45000000 0.00000004 0.00000005 0.00000004 Yes
0.00000010 0.65000000 0.00000006 0.00000007 0.00000006 Yes
0.00000010 0.85000000 0.00000008 0.00000009 0.00000008 Yes
0.00000010 0.95000000 0.00000010 0.00000009 0.00000010 Yes
0.000000001 0.500000000 0.000000000 0.000000001 0.000000000 Yes
0.000000002 0.250000000 0.000000000 0.000000001 0.000000000 Yes
0.000000005 0.100000000 0.000000000 0.000000001 0.000000000 Yes
0.000000005 0.500000000 0.000000002 0.000000003 0.000000002 Yes
0.000000005 0.900000000 0.000000004 0.000000005 0.000000004 Yes
0.000000006 0.750000000 0.000000004 0.000000005 0.000000004 Yes
0.000000009 0.500000000 0.000000004 0.000000005 0.000000004 Yes
0.000000010 0.050000000 0.000000000 0.000000001 0.000000000 Yes
0.000000010 0.250000000 0.000000002 0.000000003 0.000000002 Yes
0.000000010 0.450000000 0.000000004 0.000000005 0.000000004 Yes
0.000000010 0.650000000 0.000000006 0.000000007 0.000000006 Yes
0.000000010 0.850000000 0.000000008 0.000000009 0.000000008 Yes
0.000000010 0.950000000 0.000000010 0.000000009 0.000000010 Yes
0.0000000001 0.5000000000 0.0000000000 0.0000000001 0.0000000000 Yes
0.0000000002 0.2500000000 0.0000000000 0.0000000001 0.0000000000 Yes
0.0000000005 0.1000000000 0.0000000000 0.0000000001 0.0000000000 Yes
0.0000000005 0.5000000000 0.0000000002 0.0000000003 0.0000000002 Yes
0.0000000005 0.9000000000 0.0000000004 0.0000000005 0.0000000004 Yes
0.0000000006 0.7500000000 0.0000000004 0.0000000005 0.0000000004 Yes
0.0000000009 0.5000000000 0.0000000004 0.0000000005 0.0000000004 Yes
0.0000000010 0.0500000000 0.0000000000 0.0000000001 0.0000000000 Yes
0.0000000010 0.2500000000 0.0000000002 0.0000000003 0.0000000002 Yes
0.0000000010 0.4500000000 0.0000000004 0.0000000005 0.0000000004 Yes
0.0000000010 0.6500000000 0.0000000006 0.0000000007 0.0000000006 Yes
0.0000000010 0.8500000000 0.0000000008 0.0000000009 0.0000000008 Yes
0.00000000001 0.50000000000 0.00000000000 0.00000000001 0.00000000000 Yes
0.00000000002 0.25000000000 0.00000000000 0.00000000001 0.00000000000 Yes
0.00000000005 0.10000000000 0.00000000000 0.00000000001 0.00000000000 Yes
0.00000000005 0.50000000000 0.00000000002 0.00000000003 0.00000000002 Yes
0.00000000005 0.90000000000 0.00000000004 0.00000000005 0.00000000004 Yes
0.00000000006 0.75000000000 0.00000000004 0.00000000005 0.00000000004 Yes
0.00000000009 0.50000000000 0.00000000004 0.00000000005 0.00000000004 Yes
0.00000000010 0.05000000000 0.00000000000 0.00000000001 0.00000000000 Yes
0.00000000010 0.25000000000 0.00000000002 0.00000000003 0.00000000002 Yes
0.00000000010 0.45000000000 0.00000000004 0.00000000005 0.00000000004 Yes
0.00000000010 0.65000000000 0.00000000006 0.00000000007 0.00000000006 Yes
0.00000000010 0.85000000000 0.00000000008 0.00000000009 0.00000000008 Yes
0.000000000001 0.500000000000 0.000000000000 0.000000000001 0.000000000000 Yes
0.000000000002 0.250000000000 0.000000000000 0.000000000001 0.000000000000 Yes
0.000000000005 0.100000000000 0.000000000000 0.000000000001 0.000000000000 Yes
0.000000000005 0.300000000000 0.000000000002 0.000000000001 0.000000000002 Yes
0.000000000005 0.500000000000 0.000000000002 0.000000000003 0.000000000002 Yes
0.000000000005 0.700000000000 0.000000000004 0.000000000003 0.000000000004 Yes
0.000000000005 0.900000000000 0.000000000004 0.000000000005 0.000000000004 Yes
0.000000000006 0.750000000000 0.000000000004 0.000000000005 0.000000000004 Yes
0.000000000009 0.500000000000 0.000000000004 0.000000000005 0.000000000004 Yes
0.000000000010 0.050000000000 0.000000000000 0.000000000001 0.000000000000 Yes
0.000000000010 0.150000000000 0.000000000002 0.000000000001 0.000000000002 Yes
0.000000000010 0.250000000000 0.000000000002 0.000000000003 0.000000000002 Yes
0.000000000010 0.350000000000 0.000000000004 0.000000000003 0.000000000004 Yes
0.000000000010 0.450000000000 0.000000000004 0.000000000005 0.000000000004 Yes
0.000000000010 0.650000000000 0.000000000006 0.000000000007 0.000000000006 Yes
0.000000000010 0.850000000000 0.000000000008 0.000000000009 0.000000000008 Yes
Regards,
Bengt Richter
More information about the Python-list
mailing list