I'm trying to come up with a general way to correctly round floats with Python, given the known limitation of round()
:
Note: The behavior of
round()
for floats can be surprising: for example,round(2.675, 2)
gives2.67
instead of the expected2.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('.')[1]))
# Create trimmed float that ends with a '5' .
f = Decimal(str(f).split('.')[0] + '.' + str(f).split('.')[1][: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('.')[1])
# 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)[0] == '-' 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'
decimal
for actual production usage. \$\endgroup\$round(f, n)
(wheref
is the float andn
is the number of digits to round to) withDecimal
? \$\endgroup\$quantize
method and the description of rounding modes and the context. \$\endgroup\$