Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #############################################################################
- # Anti "float" argument generator. #
- # #
- # The goal of this script is to find a use case where #
- # using float will produce a different result then using #
- # decimal for calculation. #
- # #
- # The reason for the difference is that, while float has #
- # a pretty large precision (see EPSILON) more than enough #
- # to handle day-to-day business trasactions, the lost precision #
- # will show eventually when aggregating a not so large data set. #
- # Especially if you apply small percentages to them (like #
- # interests or taxes). #
- # #
- # The scenario simulated by this is script is: #
- # * A company has an initial balance on an interest-bearing #
- # account. #
- # * The account pays a daily interest based on an essentialy #
- # random market rate. #
- # * This is simulated a few times and eventually an error should pop. #
- # #
- # Author: Cloves Almeida (cjalmeida_at_gmail_dot_com) #
- # Copyright: None. Use at will. #
- #############################################################################
- from decimal import *
- import sys
- INITIAL_BALANCE = 116250.00
- DAYS = 3650
- TABLE_FILE = '/tmp/fdtest.txt'
- EPSILON = pow(2,-30)
- fout = open(TABLE_FILE,'w')
- prec = Decimal('.01')
- def roundf(f):
- return round(f + cmp(f,0)*EPSILON,2)
- def write_table(tbl):
- for d, s,r,e in tbl:
- write("%s\t%.2f\t%.6f\t%.2f" % (d,s,r,e))
- def find_error(tblf,tbld):
- for day in xrange(DAYS):
- f = "%.2f" % (tblf[day][3])
- d = str(tbld[day][3])
- if not f == d:
- return day
- def write(msg):
- fout.write(msg+'\n')
- def float_decimal_test():
- import random
- # generate daily interest rates
- rates = []
- for i in xrange(DAYS):
- rates.append(round(random.random() * 0.001,6))
- # store statement lines
- float_lines = []
- dec_lines = []
- ### iterate calculating daily interest using floats ###
- balance = INITIAL_BALANCE
- for i in xrange(DAYS):
- start_balance = balance
- rate = rates[i]
- interest = roundf(balance*rate)
- balance = roundf(balance + interest)
- float_lines.append((i, start_balance, rates[i], balance))
- b1 = "%.2f" % balance
- ### iterate calculating daily interest using decimals ###
- balance = Decimal(str(INITIAL_BALANCE)).quantize(prec,rounding=ROUND_HALF_UP)
- for i in xrange(DAYS):
- start_balance = balance
- rate = Decimal(str(rates[i]))
- interest = (balance * rate).quantize(prec,rounding=ROUND_HALF_UP)
- balance += interest
- dec_lines.append((i, start_balance, rate, balance))
- b2 = str(balance)
- if not b1 == b2:
- write("Float table:")
- write_table(float_lines)
- write("---")
- write("Decimal table:")
- write_table(dec_lines)
- print "There you got a use case for using decimal instead of float!"
- day = find_error(float_lines, dec_lines)
- print "Try looking at day %s in file %s" % (day,TABLE_FILE)
- raise Exception("bfloat: %s, bdec: %s" % (b1, b2))
- for x in xrange(1000):
- try:
- float_decimal_test()
- except Exception as e:
- print 'Error found in iteration %s: %s' % (x, e.message)
- fout.close()
- sys.exit(1)
- fout.close()
- print 'No error found.'
Advertisement
Add Comment
Please, Sign In to add comment