Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python round() too slow, faster way to reduce precision?

Tags:

python

I am doing the following:

TOLERANCE = 13
some_float = ...
round(some_float, TOLERANCE)

This is run many times, so performance is important. I have to round some_float due to floating point representation error. I don't actually need to "round" the number in that sense, just remove digits after 13 trailing digits.

Is there a faster way to do this?

like image 578
TaipanRex Avatar asked Jul 05 '17 08:07

TaipanRex


People also ask

How do you force round down in Python?

ceil() and math. floor() methods which rounds up and rounds down a number to the nearest whole number/integer respectively. These two methods are from the built-in math module in Python.

How do you round down 0.5 in Python?

To round a number down to the nearest 0.5:Call the math. floor() method passing it the number multiplied by 2 . Divide the result by 2 . The result of the calculation is the number rounded down to the nearest 0.5 .

How do I stop rounding decimals in Python?

It is simply always showing a fixed number of significant digits. Try import math; p=3.14; print p; p=math. pi; print p .

Why is Python rounding weird?

Straight from the documentation: 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.


2 Answers

I've made some benches to compare round(some_float, TOLERANCE) with int(some_float * p + 0.5)/p (with p as 10**TOLERANCE) and here are the results:

  • round: 6.20 seconds
  • int divide+multiply: 3.53

my bench:

import time


TOLERANCE = 5
some_float = 12.2439924563634564564564
nb_loops = 10000000

start_time = time.time()
for _ in range(nb_loops):
    r1 = round(some_float, TOLERANCE)
print(r1,time.time()-start_time)

start_time = time.time()
p = float(10**TOLERANCE)
for _ in range(nb_loops):
    r2 = int(some_float * p + 0.5)/p
print(r2,time.time()-start_time)

result:

12.24399 6.208600997924805
12.24399 3.525486946105957

so the int solution is faster. round is probably better at handling the rounding of negative numbers (as someone commented, it makes a lot extra calls, so the code is more complex). The rounding may be different depending on the sign of the input number. Accuracy vs raw speed, again.

Add 0.5 or not to round or truncate. This seems to be a detail for you, but the int solution (provided that 10**TOLERANCE is precomputed) seems faster.

If you want to use that technique, you could be tempted to put the rounding code in a function:

TOLERANCE = 5
p = float(10**TOLERANCE)
def my_round_5(some_float):
    return int(some_float * p + 0.5)/p

and call it like this:

r2 = my_round(some_float)

that would be still faster than round, but a tad slower than using the formula inline (because function call isn't free)

note that I've used p = float(10**TOLERANCE) and not p = 10**TOLERANCE so the code is compatible with python 2 (else it would truncate the decimal part due to integer division)

like image 183
Jean-François Fabre Avatar answered Nov 14 '22 22:11

Jean-François Fabre


Using int, multiply and divide is faster. You can use timeit with ipython in order to quickly benchmark Python code.

In [7]: %timeit int(1.12345678901234*(10**13))/(10.**13)
1000000 loops, best of 3: 380 ns per loop

In [8]: %timeit round(1.12345678901234, 13)
1000000 loops, best of 3: 1.32 µs per loop
like image 38
Benoît Zu Avatar answered Nov 14 '22 21:11

Benoît Zu