$search
00001 #!/usr/bin/env python 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 # Released to the public domain 28-Mar-2001, 00100 # by Tim Peters (tim.one@home.com). 00101 00102 00103 # 28-Mar-01 ver 0.0,4 00104 # Use repr() instead of str() inside __str__, because str(long) changed 00105 # since this was first written (used to produce trailing "L", doesn't 00106 # now). 00107 # 00108 # 09-May-99 ver 0,0,3 00109 # Repaired __sub__(FixedPoint, string); was blowing up. 00110 # Much more careful conversion of float (now best possible). 00111 # Implemented exact % and divmod. 00112 # 00113 # 14-Oct-98 ver 0,0,2 00114 # Added int, long, frac. Beefed up docs. Removed DECIMAL_POINT 00115 # and MINUS_SIGN globals to discourage bloating this class instead 00116 # of writing formatting wrapper classes (or subclasses) 00117 # 00118 # 11-Oct-98 ver 0,0,1 00119 # posted to c.l.py 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 # c < 0 <-> remainder < divisor/2, etc 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 # c < 0 <-> remainder < divisor/2, etc 00147 if c > 0 or (c == 0 and quotient >= 0): 00148 quotient += 1 00149 return quotient 00150 00151 # 2002-10-20 dougfort - fake classes for pre 2.2 compatibility 00152 try: 00153 object 00154 except NameError: 00155 class object: 00156 pass 00157 def property(x, y): 00158 return None 00159 00160 # The default value for the number of decimal digits carried after the 00161 # decimal point. This only has effect at compile-time. 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 # exact value is n*10**exp = n*10**(exp+p)/10**p 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 # XXX ignoring infinities and NaNs and overflows for now 00198 import math 00199 f, e = math.frexp(abs(value)) 00200 assert f == 0 or 0.5 <= f < 1.0 00201 # |value| = f * 2**e exactly 00202 00203 # Suck up CHUNK bits at a time; 28 is enough so that we suck 00204 # up all bits in 2 iterations for all known binary double- 00205 # precision formats, and small enough to fit in an int. 00206 CHUNK = 28 00207 top = 0L 00208 # invariant: |value| = (top + f) * 2**e exactly 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 # now |value| = top * 2**e exactly 00219 # want n such that n / 10**p = top * 2**e, or 00220 # n = top * 10**p * 2**e 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 # can we coerce to a float? 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 # similarly for long 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 # n1/10**p + n2/10**p = (n1+n2)/10**p 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 # n1/10**p * n2/10**p = (n1*n2/10**p)/10**p 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 # n1/10**p / (n2/10**p) = n1/n2 = (n1*10**p/n2)/10**p 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 # floor((n1/10**p)/(n2*10**p)) = floor(n1/n2) 00390 q = n1 / n2 00391 # n1/10**p - q * n2/10**p = (n1 - q * n2)/10**p 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 # 2002-10-04 dougfort - Default to Banker's Rounding for backward compatibility 00456 FixedPoint.round = bankersRounding 00457 00458 # return 10L**n 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 # same as yp 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 #print '_mkFP Debug: %s, value=%s' % (type(f),n) 00499 f.n = n 00500 f.p = p 00501 return f 00502 00503 # crud for parsing strings 00504 import re 00505 00506 # There's an optional sign at the start, and an optional exponent 00507 # at the end. The exponent has an optional sign and at least one 00508 # digit. In between, must have either at least one digit followed 00509 # by an optional fraction, or a decimal point followed by at least 00510 # one digit. Yuck. 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