HiveBrain v1.2.0
Get Started
← Back to all entries
patternpythonMinor

Workaround for the precision limitation for Python's round() function

Submitted by: @import:stackexchange-codereview··
0
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 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.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

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 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_float

Code Snippets

def format_5_dec(f):
    new_float = Decimal.quantize(f, Decimal(str(f)[:-1]), rounding='ROUND_UP')
    return new_float

Context

StackExchange Code Review Q#122243, answer score: 4

Revisions (0)

No revisions yet.