patternpythonMinor
Workaround for the precision limitation for Python's round() function
Viewed 0 times
theprecisionfunctionforlimitationpythonroundworkaround
Problem
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
I thought that a simple way to correct for this effect is to add a small value to every float that ends with a
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
round():Note: The behavior of
round() for floats can be surprising: for example, round(2.675, 2) gives 2.67 instead 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.9Below 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
Solution
Following ferada's advice I looked up the decimal module and it simplifies things quite a bit.
This is a function that does what
This is a function that does what
format_5() does but much succinctly:def format_5_dec(f):
new_float = Decimal.quantize(f, Decimal(str(f)[:-1]), rounding='ROUND_UP')
return new_floatCode Snippets
def format_5_dec(f):
new_float = Decimal.quantize(f, Decimal(str(f)[:-1]), rounding='ROUND_UP')
return new_floatContext
StackExchange Code Review Q#122243, answer score: 4
Revisions (0)
No revisions yet.