I'm trying to come up with a general way to correctly round floats with Python, given the known limitation of
Note: The behavior of
round()for floats can be surprising: for example,
2.67instead of the expected
2.68. This is not a bug: it’s a result of the fact that most decimal fractions can’t be represented exactly as a float. See Floating Point Arithmetic: Issues and Limitations for more information.
I thought that a simple way to correct for this effect is to add a small value to every float that ends with a
5, like so:
a = 7.85 b = a + 0.001 round(b, 1) > 7.9
Below is the function I've written to do this. I've tested it and it seems to work fine, but I'd like some input on possible caveats I might be overlooking (or ways to improve it)
import numpy as np from decimal import Decimal from random import randint def floats_5(N): """ Generate random floats between a random range, where all floats end with a '5'. """ rang = randint(1, 10000) flts = np.random.uniform(-1.*rang, 1.*rang, N) # Add '5' to the end of each random float, with different lengths. fl_5 =  for f in flts: # Trim float. i = randint(2, len(str(f).split('.'))) # Create trimmed float that ends with a '5' . f = Decimal(str(f).split('.') + '.' + str(f).split('.')[:i] + '5') fl_5.append(f) return fl_5 def format_5(f): """ Take float and add a decimal '1' to the end of it. Return the number of decimal paces and the new float. """ # Count number of decimal places. n = len(str(f).split('.')) # Decimal '1' to add to the end of the float. d = '0.' + '0'*n + '1' # Add or subtract depending on the sign of the float. c = -1. if str(f) == '-' else 1. # New augmented float. new_float = f + Decimal(c)*Decimal(d) # Return number of decimals and float with the small value added. return n, new_float # Get some random floats. fl_5 = floats_5(10) # Compare float, round(float), and round(new_float) print 'Original round(f) round(new_f)' for f in fl_5: n, new_float = format_5(f) print f, round(f, n-1), round(new_float, n-1), '\n'