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.
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) >>>print price * qty 1543.13 [scale is taken from left-hand operand] >>>print qty * price 1543.1250 [scale is taken from left-hand operand] >>>print Number(qty * price,2) 1543.13 [scale is taken from Number instance] -------------------- 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 -- http://mail.python.org/mailman/listinfo/python-list