19

Does anyone know of a faster decimal implementation in python?

As the example below demonstrates, the standard library's decimal module is ~100 times slower than float.

from  timeit import Timer

def run(val, the_class):
    test = the_class(1)
    for c in xrange(10000):
        d = the_class(val)
        d + test
        d - test
        d * test
        d / test
        d ** test
        str(d)
        abs(d)    

if __name__ == "__main__":
    a = Timer("run(123.345, float)", "from decimal_benchmark import run")
    print "FLOAT", a.timeit(1)
    a = Timer("run('123.345', Decimal)", "from decimal_benchmark import run; from decimal import Decimal")
    print "DECIMAL", a.timeit(1)

Outputs:

FLOAT 0.040635041427
DECIMAL 3.39666790146
damon
  • 14,485
  • 14
  • 56
  • 75
Kozyarchuk
  • 21,049
  • 14
  • 40
  • 46
  • 3
    Do you have a specific performance goal -- i.e., an algorithm that's too slow? Or, are you just hoping for hardware decimal like IBM builds in their mainframes? – S.Lott Oct 12 '08 at 12:59
  • Am I crazy or do the results become even more pronounced if you change the test value to some arbitrary float/decimal value? Like so: test = the_class(115.45678) – longda Jul 12 '12 at 18:54

5 Answers5

26

You can try cdecimal:

from cdecimal import Decimal

As of Python 3.3, the cdecimal implementation is now the built-in implementation of the decimal standard library module, so you don't need to install anything. Just use decimal.

For Python 2.7, installing cdecimal and using it instead of decimal should provide a speedup similar to what Python 3 gets by default.

user2357112
  • 260,549
  • 28
  • 431
  • 505
Andrew G
  • 817
  • 1
  • 9
  • 13
  • 7
    This is an especially good option for people who already have lots of code using the standard `decimal` module, because it is a drop-in replacement. In fact, it is included as the built-in `decimal` for Python 3.3 (released today!). – John Y Sep 29 '12 at 14:31
  • 2
    This change speed up my program by 33% in python 2.7! Not bad for a one character change! ;-) – kissgyorgy Oct 27 '13 at 15:19
  • 5
    `pip install m3-cdecimal` – a113nw Dec 03 '14 at 14:19
12

The GMP library is one of the best arbitrary precision math libraries around, and there is a Python binding available at GMPY. I would try that method.

Greg Hewgill
  • 951,095
  • 183
  • 1,149
  • 1,285
  • gmpy's mpf yields the same performance as float, it doesn't have inherent float issues such as precise compares and seems to be mostly compatible with Python Decimal interface. – Kozyarchuk Oct 13 '08 at 03:17
  • cdecimal (mentioned below) has been good to me. I don't know how accurate it is, but it does perform better than gmpy for these benchmarks - http://www.bytereef.org/mpdecimal/benchmarks.html – El Yobo Jul 21 '15 at 13:44
  • GMP doesn't do decimal math, though, so if you actually need decimal, this won't do the job. (GMP has rationals, similar to `fractions.Fraction`, but no decimal.) – user2357112 Jul 11 '18 at 02:52
3

You should compare Decimal to Long Integer performance, not floating point. Floating point is mostly hardware these days. Decimal is used for decimal precision, while Floating Point is for wider range. Use the decimal package for monetary calculations.

To quote the decimal package manual:

Decimal numbers can be represented exactly. In contrast, numbers like 1.1 do not have an exact representation in binary floating point. End users typically would not expect 1.1 to display as 1.1000000000000001 as it does with binary floating point.

The exactness carries over into arithmetic. In decimal floating point, "0.1 + 0.1 + 0.1 - 0.3" is exactly equal to zero. In binary floating point, result is 5.5511151231257827e-017. While near to zero, the differences prevent reliable equality testing and differences can accumulate. For this reason, decimal would be preferred in accounting applications which have strict equality invariants.

Community
  • 1
  • 1
gimel
  • 83,368
  • 10
  • 76
  • 104
  • 1
    well, long is actually faster than float. FLOAT 0.0551114582687 DECIMAL 3.39638546341 LONG 0.036625594419 Issue is with implementation of Python's Decimal. It holds value as a list of ints. Why not store the value as unbounded python long. Will try gmpy – Kozyarchuk Oct 12 '08 at 13:12
3

Use cDecimal.

Adding the following to your benchmark:

a = Timer("run('123.345', Decimal)", "import sys; import cdecimal; sys.modules['decimal'] = cdecimal; from decimal_benchmark import run; from decimal import Decimal")
print "CDECIMAL", a.timeit(1)

My results are:

FLOAT 0.0257983528473
DECIMAL 2.45782495288
CDECIMAL 0.0687125069413

(Python 2.7.6/32, Win7/64, AMD Athlon II 2.1GHz)

Joel Santirso
  • 161
  • 1
  • 5
0

python Decimal is very slow, one can use float or a faster implementation of Decimal cDecimal.

pranjal
  • 692
  • 1
  • 6
  • 19