ANN: Decimal.py 0.0.0
Aahz Maruch
aahz at panix.com
Fri May 18 12:20:22 EDT 2001
I've just finished stage one, addition and subtraction. Stage two is
conversion routines, multiplication, and unit tests. Stage three is
division. Right now, I'm just throwing this out in case anyone really
wants decimal arithmetic badly enough to try testing this; I don't
expect serious testing until I finish stage two.
I'm not looking for any design critiques right now, just bug reports.
Decimal.py:
'''
This is a first stab at a decimal arithmetic module based on the decimal
arithmetic ANSI standard X3.274-1996. It should work with Python 1.5.2
and later.
Most of the information used to create this module was helpfully provided
by Mike Cowlishaw's work at
http://www2.hursley.ibm.com/decimal/
The rest comes from Tim Peters' FixedPoint.py, particularly the string
conversion routine.
In the current form of Decimal(), precision is finite but unbounded, with
the exception of division. You must use the round() method to perform
any trimming.
Currently, the only way to create a Decimal instance is with a tuple
containing the necessary values. Element zero is the sign, element one
is a tuple containing the digits of the integer portion, and element
three is the exponent (an int). For example:
1 => (0, (1,), 0)
-.0123 => (1, (1,2,3), -5)
'''
DEFAULT_PRECISION = 9
class Decimal:
def __init__(self, value):
# tuple conversion
if isinstance(value, type((None,)) ):
self._sign = value[0]
self._int = value[1]
self._exp = value[2]
return
if isinstance(value, _WorkRep):
if value.sign == 1:
self._sign = 0
else:
self._sign = 1
self._int = tuple(value.int)
self._exp = value.exp
return
raise TypeError("Can't convert " + `value`)
def __repr__(self):
return "(" + `self._sign` + ", " + `self._int` + ", " + `self._exp` + ")"
def __str__(self):
import string
if self._sign:
tmp = ['-']
else:
tmp = []
for digit in self._int:
tmp.append(str(digit))
if self._exp:
tmp.append('e')
tmp.append(str(self._exp))
return string.join(tmp, '')
def __neg__(self):
if self._sign:
return Decimal( (0, self._int, self._exp) )
else:
return Decimal( (1, self._int, self._exp) )
def __abs__(self):
if self._sign:
return -self
else:
return self
def __add__(self, other):
if self._int == (0,):
return other
if other._int == (0,):
return self
op1 = _WorkRep(self)
op2 = _WorkRep(other)
op1, op2 = _normalize(op1, op2)
result = _WorkRep()
if op1.sign != op2.sign:
diff = cmp(abs(op1), abs(op2))
if diff == 0:
return Decimal( (0,(0,),0) )
if diff < 0:
op1, op2 = op2, op1
if op1.sign == -1:
result.sign = -1
op1.sign, op2.sign = op2.sign, op1.sign
else:
result.sign = 1
elif op1.sign == -1:
result.sign = -1
op1.sign, op2.sign = (1, 1)
else:
result.sign = 1
carry, loan = (0, 0)
op1.int.reverse()
op2.int.reverse()
for i in range(len(op1.int)):
tmp = op1.int[i] + (op2.sign * op2.int[i]) + carry - loan
carry, loan = (0, 0)
if tmp > 9:
carry = 1
tmp = tmp - 10
if tmp < 0:
loan = 1
tmp = tmp + 10
result.int.append(tmp)
if carry:
result.int.append(1)
if loan:
raise "What are we doing here?"
while result.int[-1] == 0:
result.int.pop()
result.int.reverse()
result.exp = op1.exp
return Decimal(result)
def __sub__(self, other):
return self.__add__(-other)
def round(self, prec=None):
raise
class _WorkRep:
def __init__(self, value=None):
if value is None:
self.sign = None
self.int = []
self.exp = None
if isinstance(value, Decimal):
if value._sign:
self.sign = -1
else:
self.sign = 1
self.int = list(value._int)
self.exp = value._exp
if isinstance(value, type((None,)) ):
self.sign = value[0]
self.int = value[1]
self.exp = value[2]
def __repr__(self):
return "(" + `self.sign` + ", " + `self.int` + ", " + `self.exp` + ")"
__str__ = __repr__
def __neg__(self):
if self.sign:
return _WorkRep( (0, self.int, self.exp) )
else:
return _WorkRep( (1, self.int, self.exp) )
def __abs__(self):
if self.sign:
return -self
else:
return self
def __cmp__(self, other):
if self.exp != other.exp:
raise ValueError("Operands not normalized: " + `self` + ", " + `other`)
if self.sign != other.sign:
if self.sign:
return -1
else:
return 1
if self.sign:
direction = -1
else:
direction = 1
int1 = self.int
int2 = other.int
if len(int1) > len(int2):
return direction * 1
if len(int1) < len(int2):
return direction * -1
for i in range(len(int1)):
if int1[i] > int2[i]:
return direction * 1
if int1[i] < int2[i]:
return direction * -1
return 0
def _normalize(op1, op2):
numdigits = op1.exp - op2.exp
if numdigits < 0:
numdigits = -numdigits
tmp = op2
else:
tmp = op1
tmp.int.extend([0] * numdigits)
tmp.exp = tmp.exp - numdigits
numdigits = len(op1.int) - len(op2.int)
if numdigits < 0:
numdigits = -numdigits
tmp = op1
else:
tmp = op2
tmp.int[0:0] = [0] * numdigits
return op1, op2
More information about the Python-list
mailing list