Mel wrote:
Frank Millman wrote:
Hi all
I have a standard requirement for a 'decimal' type, to instantiate and
manipulate numeric data that is stored in a database. I came up with a
solution long before the introduction of the Decimal type, which has
been working well for me. I know the 'scale' (number of decimal
places) of the number in advance. When I read the number in from the
database I scale it up to an integer. When I write it back I scale it
down again. All arithmetic is done using integers, so I do not lose
accuracy.
[snip]
--------------------
from __future__ import division
class Number(object):
def __init__(self,value,scale):
self.factor = 10.0**scale
if isinstance(value,Number):
value = value.value / value.factor
I think this could lead to trouble. One complaint against binary floating
point is that it messes up low-order decimal digits, and this ensures that
all calculations are effectively done in binary floating point. Better, I
think would be
if isinstance (value, Number):
self.value = value.value
self.scale = scale + value.scale
and be done with it. Of course, this means self.scale no longer gives the
preferred number of fractional digits. My bias: I did a DecimalFloat class
way back when, when Decimal was being discussed, and separated the exponent
for calculations from the rounding precision for display.
What about a little rewrite so the current implementation is like the
original, and all calculations are done as integers? Or is this just
getting closer and closer to what Decimal does?
[only lightly tested]
from __future__ import division
class Number(object):
def __init__(self, value, scale):
if isinstance(value, Number):
delta = value.scale - scale
if delta > 0:
self.value = value.value // 10**delta
elif delta < 0:
self.value = value.value * 10**abs(delta)
else:
self.value = value.value
else:
if not scale:
scale += 1
self.scale = scale
self.factor = 10**self.scale
self.value = long(round(value * self.factor))
def __add__(self, other):
answer = Number(other, self.scale)
answer.value += self.value
return answer
def __sub__(self, rhs):
answer = Number(rhs, self.scale)
answer.value = self.value - answer.value
return answer
def __mul__(self, other):
answer = Number(other, self.scale)
answer.value *= self.value
answer.value //= answer.factor
return answer
def __truediv__(self, rhs):
answer = Number(rhs, self.scale)
quotient = 0
divisor = answer.value
dividend = self.value
for i in range(self.scale+1):
quotient = (quotient * 10) + (dividend // divisor)
dividend = (dividend % divisor) * 10
answer.value = quotient
return answer
def __radd__(self, lhs):
return self.__add__(lhs)
def __rsub__(self, lhs):
answer = Number(lhs, self.scale)
answer.value = answer.value - self.value
return answer
def __rmul__(self, lhs):
return self.__mul__(lhs)
def __rtruediv__(self, lhs):
answer = Number(lhs, self.scale)
quotient = 0
divisor = self.value
dividend = answer.value
for i in range(self.scale+1):
quotient = (quotient * 10) + (dividend // divisor)
dividend = (dividend % divisor) * 10
answer.value = quotient
return answer
def __cmp__(self, rhs):
other = Number(rhs, self.scale)
if self.value < other.value:
return -1
elif self.value > other.value:
return 1
else:
return 0
def __str__(self):
s = str(self.value)
if s[0] == '-':
minus = '-'
s = s[1:].zfill(self.scale+1)
else:
minus = ''
s = s.zfill(self.scale+1)
return '%s%s.%s' % (minus, s[:-self.scale], s[-self.scale:])
--
Ethan
--
http://mail.python.org/mailman/listinfo/python-list