long(Decimal) performance

A

ajaksu

Hello c.l.p.ers :)

Running long(Decimal) is pretty slow, and the conversion is based on
strings. I'm trying to figure out whether there is a good reason for
using strings like in decimal.py (that reason would be bound to bite me
down the road).
This converts Decimal to long and is much faster in my test system
(PIII 650MHz, but was written on a P133 a year ago :)).

def dec2long(number):
"""
Convert decimal.Decimal to long.

Hopefully faster than C{int(Decimal())}.
@param number: A C{decimal.Decimal} value
@return: Long from input
"""
if not isinstance(number, Decimal):
raise TypeError, "dec2long requires an instance of Decimal"
elif number._is_special:
if number._isnan():
raise TypeError, "This Decimal is NaN, an ex-Number"
elif number._isinfinity():
raise OverflowError, "Cannot convert infinity to long"

else:
longstring = str(number)
if "e" in longstring:
longsplit = longstring.split("e")
elif "E" in longstring:
longsplit = longstring.split("E")
else:
longsplit = [longstring, "0"]
floatexp = long(len(longsplit[0].split(".")[1]))
ftol = long(Decimal(longsplit[0]) * 10L**floatexp)
longexp = long(int(longsplit[1]) - floatexp)
result = ftol * 10L**longexp
return result

For the sake of camparison, here's decimal.py __int__:

def __int__(self):
"""Converts self to an int, truncating if necessary."""
[snip: error checking]
if self._exp >= 0:
s = ''.join(map(str, self._int)) + '0'*self._exp
else:
s = ''.join(map(str, self._int))[:self._exp]
if s == '':
s = '0'
sign = '-'*self._sign
return int(sign + s)

Then, some timings:
Decimal("7.252714899122810148399426210E+12378")
10 loops, best of 3: 12 ms per loop
10 loops, best of 3: 283 ms per loop
True

Some anachronisms (like 10L) and very probably mistakes are present in
that old hack, but it makes decimal somewhat more interesting for me.

The answer to this message might be "decimal will be written in C very
soon, so nevermind", but I'd love to hear that in fact the following
function is wrong and there is a good reason long(Decimal) works based
on strings... or that my function is silly but could be corrected.

Thanks in advance and best regards,
Daniel 'ajaksu' Diniz

PS: my use case is Stirling's approximation of the factorial for large
numbers, feel free to criticize that, too ;)
 
A

ajaksu

Sorry... I'm ashamed to submit such awful code in my first post. Let me
try again...

from decimal import Decimal
def dec2long(number):
""" Convert decimal.Decimal to long """
longstring = str(number)
if "e" in longstring:
radix, exponent = longstring.split("e")
elif "E" in longstring:
radix, exponent = longstring.split("E")
else:
radix, exponent = [longstring, "0"]
floatexp = long(len(radix.split(".")[1]))
floatish = Decimal(radix) * 10L**floatexp
ftol = long(floatish)
longexp = long(int(exponent) - floatexp)
return ftol * 10L**longexp

This one should run by itself, is more readable and... still smells bad
:(
Sorry again.
 
A

Aahz

Running long(Decimal) is pretty slow, and the conversion is based on
strings. I'm trying to figure out whether there is a good reason for
using strings like in decimal.py (that reason would be bound to bite me
down the road).

I'm not sure why it's coded that, but it's somewhat irrelevant: right
now, work is being done to convert decimal.py to C code, which will
almost certainly be much faster than your code. Generally speaking, you
should not be using Decimal now with any expectation of speed.
--
Aahz ([email protected]) <*> http://www.pythoncraft.com/

"Debugging is twice as hard as writing the code in the first place.
Therefore, if you write the code as cleverly as possible, you are, by
definition, not smart enough to debug it." --Brian W. Kernighan
 
A

ajaksu

Hi Aahz, thanks for the feedback!
I'm not sure why it's coded that, but it's somewhat irrelevant: right
now, work is being done to convert decimal.py to C code, which will
almost certainly be much faster than your code. Generally speaking, you
should not be using Decimal now with any expectation of speed.

Agreed and agreed.

Just to avoid that the buggy (and unreadable) versions above harm
anyone, here's a better attempt:

def dec2long(number):
""" Convert C{decimal.Decimal} to long """
decimal_string = str(number)
## Split 123.45E10 -> radix = 123.45, exponent = 10
if "e" in decimal_string:
radix, exponent = decimal_string.split("e")
elif "E" in decimal_string:
radix, exponent = decimal_string.split("E")
else:
radix, exponent = (decimal_string, 0)
if exponent:
exponent = int(exponent)
if "." in radix:
## radix = 123.45, radix_decimal_part_len = 2
radix_decimal_part_len = long(len(radix.split(".")[1]))
## radix = 123.45, radix_as_long = 123.45 * 10**2 = 12345
radix_as_long = long(Decimal(radix) *
(10L**radix_decimal_part_len))
##corrected_exponent = 10 - 2 = 8
corrected_exponent = exponent - radix_decimal_part_len
## return 12345 * 10**8
result = radix_as_long * 10L** corrected_exponent
else:
radix_as_long = long(radix)
result = radix_as_long * 10L**exponent
else:
if "." in radix:
radix_integer_part = long(radix.split(".")[0])
else:
radix_integer_part = long(radix)
result = radix_integer_part
return result


Working from inside decimal.py allows things to work faster, but anyone
wanting speed REALLY shouldn't use Decimal :). Now I'm trying clnum
("Rational and arbitrary precision floating point numbers"):
http://cheeseshop.python.org/pypi/clnum/1.2

Cheers,
Daniel
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,774
Messages
2,569,596
Members
45,143
Latest member
SterlingLa
Top