00001
00002 """
00003 FixedPoint objects support decimal arithmetic with a fixed number of
00004 digits (called the object's precision) after the decimal point. The
00005 number of digits before the decimal point is variable & unbounded.
00006
00007 The precision is user-settable on a per-object basis when a FixedPoint
00008 is constructed, and may vary across FixedPoint objects. The precision
00009 may also be changed after construction via FixedPoint.set_precision(p).
00010 Note that if the precision of a FixedPoint is reduced via set_precision,
00011 information may be lost to rounding.
00012
00013 >>> x = FixedPoint("5.55") # precision defaults to 2
00014 >>> print x
00015 5.55
00016 >>> x.set_precision(1) # round to one fraction digit
00017 >>> print x
00018 5.6
00019 >>> print FixedPoint("5.55", 1) # same thing setting to 1 in constructor
00020 5.6
00021 >>> repr(x) # returns constructor string that reproduces object exactly
00022 "FixedPoint('5.6', 1)"
00023 >>>
00024
00025 When FixedPoint objects of different precision are combined via + - * /,
00026 the result is computed to the larger of the inputs' precisions, which also
00027 becomes the precision of the resulting FixedPoint object.
00028
00029 >>> print FixedPoint("3.42") + FixedPoint("100.005", 3)
00030 103.425
00031 >>>
00032
00033 When a FixedPoint is combined with other numeric types (ints, floats,
00034 strings representing a number) via + - * /, then similarly the computation
00035 is carried out using-- and the result inherits --the FixedPoint's
00036 precision.
00037
00038 >>> print FixedPoint(1) / 7
00039 0.14
00040 >>> print FixedPoint(1, 30) / 7
00041 0.142857142857142857142857142857
00042 >>>
00043
00044 The string produced by str(x) (implictly invoked by "print") always
00045 contains at least one digit before the decimal point, followed by a
00046 decimal point, followed by exactly x.get_precision() digits. If x is
00047 negative, str(x)[0] == "-".
00048
00049 The FixedPoint constructor can be passed an int, long, string, float,
00050 FixedPoint, or any object convertible to a float via float() or to a
00051 long via long(). Passing a precision is optional; if specified, the
00052 precision must be a non-negative int. There is no inherent limit on
00053 the size of the precision, but if very very large you'll probably run
00054 out of memory.
00055
00056 Note that conversion of floats to FixedPoint can be surprising, and
00057 should be avoided whenever possible. Conversion from string is exact
00058 (up to final rounding to the requested precision), so is greatly
00059 preferred.
00060
00061 >>> print FixedPoint(1.1e30)
00062 1099999999999999993725589651456.00
00063 >>> print FixedPoint("1.1e30")
00064 1100000000000000000000000000000.00
00065 >>>
00066
00067 The following Python operators and functions accept FixedPoints in the
00068 expected ways:
00069
00070 binary + - * / % divmod
00071 with auto-coercion of other types to FixedPoint.
00072 + - % divmod of FixedPoints are always exact.
00073 * / of FixedPoints may lose information to rounding, in
00074 which case the result is the infinitely precise answer
00075 rounded to the result's precision.
00076 divmod(x, y) returns (q, r) where q is a long equal to
00077 floor(x/y) as if x/y were computed to infinite precision,
00078 and r is a FixedPoint equal to x - q * y; no information
00079 is lost. Note that q has the sign of y, and abs(r) < abs(y).
00080 unary -
00081 == != < > <= >= cmp
00082 min max
00083 float int long (int and long truncate)
00084 abs
00085 str repr
00086 hash
00087 use as dict keys
00088 use as boolean (e.g. "if some_FixedPoint:" -- true iff not zero)
00089
00090 Methods unique to FixedPoints:
00091 .copy() return new FixedPoint with same value
00092 .frac() long(x) + x.frac() == x
00093 .get_precision() return the precision(p) of this FixedPoint object
00094 .set_precision(p) set the precision of this FixedPoint object
00095
00096 Provided as-is; use at your own risk; no warranty; no promises; enjoy!
00097 """
00098
00099
00100
00101
00102
00103
00104
00105
00106
00107
00108
00109
00110
00111
00112
00113
00114
00115
00116
00117
00118
00119
00120
00121 __copyright__ = "Copyright (C) Python Software Foundation"
00122 __author__ = "Tim Peters"
00123 __version__ = 0, 1, 0
00124
00125 def bankersRounding(self, dividend, divisor, quotient, remainder):
00126 """
00127 rounding via nearest-even
00128 increment the quotient if
00129 the remainder is more than half of the divisor
00130 or the remainder is exactly half the divisor and the quotient is odd
00131 """
00132 c = cmp(remainder << 1, divisor)
00133
00134 if c > 0 or (c == 0 and (quotient & 1) == 1):
00135 quotient += 1
00136 return quotient
00137
00138 def addHalfAndChop(self, dividend, divisor, quotient, remainder):
00139 """
00140 the equivalent of 'add half and chop'
00141 increment the quotient if
00142 the remainder is greater than half of the divisor
00143 or the remainder is exactly half the divisor and the quotient is >= 0
00144 """
00145 c = cmp(remainder << 1, divisor)
00146
00147 if c > 0 or (c == 0 and quotient >= 0):
00148 quotient += 1
00149 return quotient
00150
00151
00152 try:
00153 object
00154 except NameError:
00155 class object:
00156 pass
00157 def property(x, y):
00158 return None
00159
00160
00161
00162 DEFAULT_PRECISION = 2
00163
00164 class FixedPoint(object):
00165 """Basic FixedPoint object class,
00166 The exact value is self.n / 10**self.p;
00167 self.n is a long; self.p is an int
00168 """
00169 __slots__ = ['n', 'p']
00170 def __init__(self, value=0, precision=DEFAULT_PRECISION):
00171 self.n = self.p = 0
00172 self.set_precision(precision)
00173 p = self.p
00174
00175 if isinstance(value, type("42.3e5")):
00176 n, exp = _string2exact(value)
00177
00178 effective_exp = exp + p
00179 if effective_exp > 0:
00180 n = n * _tento(effective_exp)
00181 elif effective_exp < 0:
00182 n = self._roundquotient(n, _tento(-effective_exp))
00183 self.n = n
00184 return
00185
00186 if isinstance(value, type(42)) or isinstance(value, type(42L)):
00187 self.n = long(value) * _tento(p)
00188 return
00189
00190 if isinstance(value, type(self)):
00191 temp = value.copy()
00192 temp.set_precision(p)
00193 self.n, self.p = temp.n, temp.p
00194 return
00195
00196 if isinstance(value, type(42.0)):
00197
00198 import math
00199 f, e = math.frexp(abs(value))
00200 assert f == 0 or 0.5 <= f < 1.0
00201
00202
00203
00204
00205
00206 CHUNK = 28
00207 top = 0L
00208
00209 while f:
00210 f = math.ldexp(f, CHUNK)
00211 digit = int(f)
00212 assert digit >> CHUNK == 0
00213 top = (top << CHUNK) | digit
00214 f = f - digit
00215 assert 0.0 <= f < 1.0
00216 e = e - CHUNK
00217
00218
00219
00220
00221 top = top * _tento(p)
00222 if e >= 0:
00223 n = top << e
00224 else:
00225 n = self._roundquotient(top, 1L << -e)
00226 if value < 0:
00227 n = -n
00228 self.n = n
00229 return
00230
00231 if isinstance(value, type(42-42j)):
00232 raise TypeError("can't convert complex to FixedPoint: " +
00233 `value`)
00234
00235
00236 yes = 1
00237 try:
00238 asfloat = float(value)
00239 except:
00240 yes = 0
00241 if yes:
00242 self.__init__(asfloat, p)
00243 return
00244
00245
00246 yes = 1
00247 try:
00248 aslong = long(value)
00249 except:
00250 yes = 0
00251 if yes:
00252 self.__init__(aslong, p)
00253 return
00254
00255 raise TypeError("can't convert to FixedPoint: " + `value`)
00256
00257 def get_precision(self):
00258 """Return the precision of this FixedPoint.
00259
00260 The precision is the number of decimal digits carried after
00261 the decimal point, and is an int >= 0.
00262 """
00263
00264 return self.p
00265
00266 def set_precision(self, precision=DEFAULT_PRECISION):
00267 """Change the precision carried by this FixedPoint to p.
00268
00269 precision must be an int >= 0, and defaults to
00270 DEFAULT_PRECISION.
00271
00272 If precision is less than this FixedPoint's current precision,
00273 information may be lost to rounding.
00274 """
00275
00276 try:
00277 p = int(precision)
00278 except:
00279 raise TypeError("precision not convertable to int: " +
00280 `precision`)
00281 if p < 0:
00282 raise ValueError("precision must be >= 0: " + `precision`)
00283
00284 if p > self.p:
00285 self.n = self.n * _tento(p - self.p)
00286 elif p < self.p:
00287 self.n = self._roundquotient(self.n, _tento(self.p - p))
00288 self.p = p
00289
00290 precision = property(get_precision, set_precision)
00291
00292 def __str__(self):
00293 n, p = self.n, self.p
00294 i, f = divmod(abs(n), _tento(p))
00295 if p:
00296 frac = repr(f)[:-1]
00297 frac = "0" * (p - len(frac)) + frac
00298 else:
00299 frac = ""
00300 return "-"[:n<0] + \
00301 repr(i)[:-1] + \
00302 "." + frac
00303
00304 def __repr__(self):
00305 return "FixedPoint" + `(str(self), self.p)`
00306
00307 def copy(self):
00308 return _mkFP(self.n, self.p, type(self))
00309
00310 __copy__ = copy
00311
00312 def __deepcopy__(self, memo):
00313 return self.copy()
00314
00315 def __cmp__(self, other):
00316 xn, yn, p = _norm(self, other, FixedPoint=type(self))
00317 return cmp(xn, yn)
00318
00319 def __hash__(self):
00320 """ Caution! == values must have equal hashes, and a FixedPoint
00321 is essentially a rational in unnormalized form. There's
00322 really no choice here but to normalize it, so hash is
00323 potentially expensive.
00324 n, p = self.__reduce()
00325
00326 Obscurity: if the value is an exact integer, p will be 0 now,
00327 so the hash expression reduces to hash(n). So FixedPoints
00328 that happen to be exact integers hash to the same things as
00329 their int or long equivalents. This is Good. But if a
00330 FixedPoint happens to have a value exactly representable as
00331 a float, their hashes may differ. This is a teensy bit Bad.
00332 """
00333 n, p = self.__reduce()
00334 return hash(n) ^ hash(p)
00335
00336 def __nonzero__(self):
00337 """ Returns true if this FixedPoint is not equal to zero"""
00338 return self.n != 0
00339
00340 def __neg__(self):
00341 return _mkFP(-self.n, self.p, type(self))
00342
00343 def __abs__(self):
00344 """ Returns new FixedPoint containing the absolute value of this FixedPoint"""
00345 if self.n >= 0:
00346 return self.copy()
00347 else:
00348 return -self
00349
00350 def __add__(self, other):
00351 n1, n2, p = _norm(self, other, FixedPoint=type(self))
00352
00353 return _mkFP(n1 + n2, p, type(self))
00354
00355 __radd__ = __add__
00356
00357 def __sub__(self, other):
00358 if not isinstance(other, type(self)):
00359 other = type(self)(other, self.p)
00360 return self.__add__(-other)
00361
00362 def __rsub__(self, other):
00363 return (-self) + other
00364
00365 def __mul__(self, other):
00366 n1, n2, p = _norm(self, other, FixedPoint=type(self))
00367
00368 return _mkFP(self._roundquotient(n1 * n2, _tento(p)), p, type(self))
00369
00370 __rmul__ = __mul__
00371
00372 def __div__(self, other):
00373 n1, n2, p = _norm(self, other, FixedPoint=type(self))
00374 if n2 == 0:
00375 raise ZeroDivisionError("FixedPoint division")
00376 if n2 < 0:
00377 n1, n2 = -n1, -n2
00378
00379 return _mkFP(self._roundquotient(n1 * _tento(p), n2), p, type(self))
00380
00381 def __rdiv__(self, other):
00382 n1, n2, p = _norm(self, other, FixedPoint=type(self))
00383 return _mkFP(n2, p, FixedPoint=type(self)) / self
00384
00385 def __divmod__(self, other):
00386 n1, n2, p = _norm(self, other, FixedPoint=type(self))
00387 if n2 == 0:
00388 raise ZeroDivisionError("FixedPoint modulo")
00389
00390 q = n1 / n2
00391
00392 return q, _mkFP(n1 - q * n2, p, type(self))
00393
00394 def __rdivmod__(self, other):
00395 n1, n2, p = _norm(self, other, FixedPoint=type(self))
00396 return divmod(_mkFP(n2, p), self)
00397
00398 def __mod__(self, other):
00399 return self.__divmod__(other)[1]
00400
00401 def __rmod__(self, other):
00402 n1, n2, p = _norm(self, other, FixedPoint=type(self))
00403 return _mkFP(n2, p, type(self)).__mod__(self)
00404
00405 def __float__(self):
00406 """Return the floating point representation of this FixedPoint.
00407 Caution! float can lose precision.
00408 """
00409 n, p = self.__reduce()
00410 return float(n) / float(_tento(p))
00411
00412 def __long__(self):
00413 """EJG/DF - Should this round instead?
00414 Note e.g. long(-1.9) == -1L and long(1.9) == 1L in Python
00415 Note that __int__ inherits whatever __long__ does,
00416 and .frac() is affected too
00417 """
00418 answer = abs(self.n) / _tento(self.p)
00419 if self.n < 0:
00420 answer = -answer
00421 return answer
00422
00423 def __int__(self):
00424 """Return integer value of FixedPoint object."""
00425 return int(self.__long__())
00426
00427 def frac(self):
00428 """Return fractional portion as a FixedPoint.
00429
00430 x.frac() + long(x) == x
00431 """
00432 return self - long(self)
00433
00434 def _roundquotient(self, x, y):
00435 """
00436 Divide x by y,
00437 return the result of rounding
00438 Developers may substitute their own 'round' for custom rounding
00439 y must be > 0
00440 """
00441 assert y > 0
00442 n, leftover = divmod(x, y)
00443 return self.round(x, y, n, leftover)
00444
00445 def __reduce(self):
00446 """ Return n, p s.t. self == n/10**p and n % 10 != 0"""
00447 n, p = self.n, self.p
00448 if n == 0:
00449 p = 0
00450 while p and n % 10 == 0:
00451 p = p - 1
00452 n = n / 10
00453 return n, p
00454
00455
00456 FixedPoint.round = bankersRounding
00457
00458
00459
00460 def _tento(n, cache={}):
00461 """Cached computation of 10**n"""
00462 try:
00463 return cache[n]
00464 except KeyError:
00465 answer = cache[n] = 10L ** n
00466 return answer
00467
00468 def _norm(x, y, isinstance=isinstance, FixedPoint=FixedPoint,
00469 _tento=_tento):
00470 """Return xn, yn, p s.t.
00471 p = max(x.p, y.p)
00472 x = xn / 10**p
00473 y = yn / 10**p
00474
00475 x must be FixedPoint to begin with; if y is not FixedPoint,
00476 it inherits its precision from x.
00477
00478 Note that this method is called a lot, so default-arg tricks are helpful.
00479 """
00480 assert isinstance(x, FixedPoint)
00481 if not isinstance(y, FixedPoint):
00482 y = FixedPoint(y, x.p)
00483 xn, yn = x.n, y.n
00484 xp, yp = x.p, y.p
00485 if xp > yp:
00486 yn = yn * _tento(xp - yp)
00487 p = xp
00488 elif xp < yp:
00489 xn = xn * _tento(yp - xp)
00490 p = yp
00491 else:
00492 p = xp
00493 return xn, yn, p
00494
00495 def _mkFP(n, p, FixedPoint=FixedPoint):
00496 """Make FixedPoint objext - Return a new FixedPoint object with the selected precision."""
00497 f = FixedPoint()
00498
00499 f.n = n
00500 f.p = p
00501 return f
00502
00503
00504 import re
00505
00506
00507
00508
00509
00510
00511
00512 _parser = re.compile(r"""
00513 \s*
00514 (?P<sign>[-+])?
00515 (
00516 (?P<int>\d+) (\. (?P<frac>\d*))?
00517 |
00518 \. (?P<onlyfrac>\d+)
00519 )
00520 ([eE](?P<exp>[-+]? \d+))?
00521 \s* $
00522 """, re.VERBOSE).match
00523
00524 del re
00525
00526
00527 def _string2exact(s):
00528 """Return n, p s.t. float string value == n * 10**p exactly."""
00529 m = _parser(s)
00530 if m is None:
00531 raise ValueError("can't parse as number: " + `s`)
00532
00533 exp = m.group('exp')
00534 if exp is None:
00535 exp = 0
00536 else:
00537 exp = int(exp)
00538
00539 intpart = m.group('int')
00540 if intpart is None:
00541 intpart = "0"
00542 fracpart = m.group('onlyfrac')
00543 else:
00544 fracpart = m.group('frac')
00545 if fracpart is None or fracpart == "":
00546 fracpart = "0"
00547 assert intpart
00548 assert fracpart
00549
00550 i, f = long(intpart), long(fracpart)
00551 nfrac = len(fracpart)
00552 i = i * _tento(nfrac) + f
00553 exp = exp - nfrac
00554
00555 if m.group('sign') == "-":
00556 i = -i
00557
00558 return i, exp
00559
00560 def _test():
00561 """Unit testing framework"""
00562 fp = FixedPoint
00563 o = fp("0.1")
00564 assert str(o) == "0.10"
00565 t = fp("-20e-2", 5)
00566 assert str(t) == "-0.20000"
00567 assert t < o
00568 assert o > t
00569 assert min(o, t) == min(t, o) == t
00570 assert max(o, t) == max(t, o) == o
00571 assert o != t
00572 assert --t == t
00573 assert abs(t) > abs(o)
00574 assert abs(o) < abs(t)
00575 assert o == o and t == t
00576 assert t.copy() == t
00577 assert o == -t/2 == -.5 * t
00578 assert abs(t) == o + o
00579 assert abs(o) == o
00580 assert o/t == -0.5
00581 assert -(t/o) == (-t)/o == t/-o == 2
00582 assert 1 + o == o + 1 == fp(" +00.000011e+5 ")
00583 assert 1/o == 10
00584 assert o + t == t + o == -o
00585 assert 2.0 * t == t * 2 == "2" * t == o/o * 2L * t
00586 assert 1 - t == -(t - 1) == fp(6L)/5
00587 assert t*t == 4*o*o == o*4*o == o*o*4
00588 assert fp(2) - "1" == 1
00589 assert float(-1/t) == 5.0
00590 for p in range(20):
00591 assert 42 + fp("1e-20", p) - 42 == 0
00592 assert 1/(42 + fp("1e-20", 20) - 42) == fp("100.0E18")
00593 o = fp(".9995", 4)
00594 assert 1 - o == fp("5e-4", 10)
00595 o.set_precision(3)
00596 assert o == 1
00597 o = fp(".9985", 4)
00598 o.set_precision(3)
00599 assert o == fp(".998", 10)
00600 assert o == o.frac()
00601 o.set_precision(100)
00602 assert o == fp(".998", 10)
00603 o.set_precision(2)
00604 assert o == 1
00605 x = fp(1.99)
00606 assert long(x) == -long(-x) == 1L
00607 assert int(x) == -int(-x) == 1
00608 assert x == long(x) + x.frac()
00609 assert -x == long(-x) + (-x).frac()
00610 assert fp(7) % 4 == 7 % fp(4) == 3
00611 assert fp(-7) % 4 == -7 % fp(4) == 1
00612 assert fp(-7) % -4 == -7 % fp(-4) == -3
00613 assert fp(7.0) % "-4.0" == 7 % fp(-4) == -1
00614 assert fp("5.5") % fp("1.1") == fp("5.5e100") % fp("1.1e100") == 0
00615 assert divmod(fp("1e100"), 3) == (long(fp("1e100")/3), 1)
00616
00617 if __name__ == '__main__':
00618 _test()
00619