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.

There is one inconvenience with this approach. For example, if I have

a product quantity with a scale of 4, and a price with a scale of 2,

and I want to multiply them to get a value with a scale of 2, I have

to remember to scale the result down by 4. This is a minor chore, and

errors are quickly picked up by testing, but it does make the code a

bit messy, so it would be nice to find a solution.

I am now doing some refactoring, and decided to take a look at the

Decimal type. My initial impressions are that it is quite awkward to

use, that I do not need its advanced features, and that it does not

help solve the one problem I have mentioned above.

I therefore spent a bit of time experimenting with a Number type that

suits my particular requirements. I have come up with something that

seems to work, which I show below.

I have two questions.

1. Are there any obvious problems in what I have done?

2. Am I reinventing the wheel unnecessarily? i.e. can I do the

equivalent quite easily using the Decimal type?

--------------------

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

self.value = long(round(value * self.factor))

self.scale = scale

def __add__(self,other):

if isinstance(other,Number):

other = other.value / other.factor

return Number((self.value/self.factor)+other,self.scale)

def __sub__(self,other):

if isinstance(other,Number):

other = other.value / other.factor

return Number((self.value/self.factor)-other,self.scale)

def __mul__(self,other):

if isinstance(other,Number):

other = other.value / other.factor

return Number((self.value/self.factor)*other,self.scale)

def __truediv__(self,other):

if isinstance(other,Number):

other = other.value / other.factor

return Number((self.value/self.factor)/other,self.scale)

def __radd__(self,other):

return self.__add__(other)

def __rsub__(self,other):

return Number(other-(self.value/self.factor),self.scale)

def __rmul__(self,other):

return self.__mul__(other)

def __rtruediv__(self,other):

return Number(other/(self.value/self.factor),self.scale)

def __cmp__(self,other):

if isinstance(other,Number):

other = other.value / other.factor

this = self.value / self.factor

if this < other:

return -1

elif this other:

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:])

--------------------

Example usage -

>>>qty = Number(12.5,4)

price = Number(123.45,2)

1543.13 [scale is taken from left-hand operand]>>>print price * qty

1543.1250 [scale is taken from left-hand operand]>>>print qty * price

1543.13 [scale is taken from Number instance]>>>print Number(qty * price,2)

--------------------

At this stage I have not built in any rounding options, but this can

be done later if I find that I need it.

Any comments will be welcome.

Thanks

Frank Millman