3 FixedPoint objects support decimal arithmetic with a fixed number of 4 digits (called the object's precision) after the decimal point. The 5 number of digits before the decimal point is variable & unbounded. 7 The precision is user-settable on a per-object basis when a FixedPoint 8 is constructed, and may vary across FixedPoint objects. The precision 9 may also be changed after construction via FixedPoint.set_precision(p). 10 Note that if the precision of a FixedPoint is reduced via set_precision, 11 information may be lost to rounding. 13 >>> x = FixedPoint("5.55") # precision defaults to 2 16 >>> x.set_precision(1) # round to one fraction digit 19 >>> print FixedPoint("5.55", 1) # same thing setting to 1 in constructor 21 >>> repr(x) # returns constructor string that reproduces object exactly 22 "FixedPoint('5.6', 1)" 25 When FixedPoint objects of different precision are combined via + - * /, 26 the result is computed to the larger of the inputs' precisions, which also 27 becomes the precision of the resulting FixedPoint object. 29 >>> print FixedPoint("3.42") + FixedPoint("100.005", 3) 33 When a FixedPoint is combined with other numeric types (ints, floats, 34 strings representing a number) via + - * /, then similarly the computation 35 is carried out using-- and the result inherits --the FixedPoint's 38 >>> print FixedPoint(1) / 7 40 >>> print FixedPoint(1, 30) / 7 41 0.142857142857142857142857142857 44 The string produced by str(x) (implictly invoked by "print") always 45 contains at least one digit before the decimal point, followed by a 46 decimal point, followed by exactly x.get_precision() digits. If x is 47 negative, str(x)[0] == "-". 49 The FixedPoint constructor can be passed an int, long, string, float, 50 FixedPoint, or any object convertible to a float via float() or to a 51 long via long(). Passing a precision is optional; if specified, the 52 precision must be a non-negative int. There is no inherent limit on 53 the size of the precision, but if very very large you'll probably run 56 Note that conversion of floats to FixedPoint can be surprising, and 57 should be avoided whenever possible. Conversion from string is exact 58 (up to final rounding to the requested precision), so is greatly 61 >>> print FixedPoint(1.1e30) 62 1099999999999999993725589651456.00 63 >>> print FixedPoint("1.1e30") 64 1100000000000000000000000000000.00 67 The following Python operators and functions accept FixedPoints in the 70 binary + - * / % divmod 71 with auto-coercion of other types to FixedPoint. 72 + - % divmod of FixedPoints are always exact. 73 * / of FixedPoints may lose information to rounding, in 74 which case the result is the infinitely precise answer 75 rounded to the result's precision. 76 divmod(x, y) returns (q, r) where q is a long equal to 77 floor(x/y) as if x/y were computed to infinite precision, 78 and r is a FixedPoint equal to x - q * y; no information 79 is lost. Note that q has the sign of y, and abs(r) < abs(y). 83 float int long (int and long truncate) 88 use as boolean (e.g. "if some_FixedPoint:" -- true iff not zero) 90 Methods unique to FixedPoints: 91 .copy() return new FixedPoint with same value 92 .frac() long(x) + x.frac() == x 93 .get_precision() return the precision(p) of this FixedPoint object 94 .set_precision(p) set the precision of this FixedPoint object 96 Provided as-is; use at your own risk; no warranty; no promises; enjoy! 121 __copyright__ =
"Copyright (C) Python Software Foundation" 122 __author__ =
"Tim Peters" 123 __version__ = 0, 1, 0
127 rounding via nearest-even 128 increment the quotient if 129 the remainder is more than half of the divisor 130 or the remainder is exactly half the divisor and the quotient is odd 132 c = cmp(remainder << 1, divisor)
134 if c > 0
or (c == 0
and (quotient & 1) == 1):
140 the equivalent of 'add half and chop' 141 increment the quotient if 142 the remainder is greater than half of the divisor 143 or the remainder is exactly half the divisor and the quotient is >= 0 145 c = cmp(remainder << 1, divisor)
147 if c > 0
or (c == 0
and quotient >= 0):
162 DEFAULT_PRECISION = 2
165 """Basic FixedPoint object class, 166 The exact value is self.n / 10**self.p; 167 self.n is a long; self.p is an int 169 __slots__ = [
'n',
'p']
170 def __init__(self, value=0, precision=DEFAULT_PRECISION):
175 if isinstance(value, type(
"42.3e5")):
178 effective_exp = exp + p
179 if effective_exp > 0:
180 n = n *
_tento(effective_exp)
181 elif effective_exp < 0:
186 if isinstance(value, type(42))
or isinstance(value, type(42L)):
187 self.
n = long(value) *
_tento(p)
190 if isinstance(value, type(self)):
192 temp.set_precision(p)
193 self.
n, self.
p = temp.n, temp.p
196 if isinstance(value, type(42.0)):
199 f, e = math.frexp(abs(value))
200 assert f == 0
or 0.5 <= f < 1.0
210 f = math.ldexp(f, CHUNK)
212 assert digit >> CHUNK == 0
213 top = (top << CHUNK) | digit
215 assert 0.0 <= f < 1.0
231 if isinstance(value, type(42-42j)):
232 raise TypeError(
"can't convert complex to FixedPoint: " +
238 asfloat = float(value)
255 raise TypeError(
"can't convert to FixedPoint: " + `value`)
258 """Return the precision of this FixedPoint. 260 The precision is the number of decimal digits carried after 261 the decimal point, and is an int >= 0. 267 """Change the precision carried by this FixedPoint to p. 269 precision must be an int >= 0, and defaults to 272 If precision is less than this FixedPoint's current precision, 273 information may be lost to rounding. 279 raise TypeError(
"precision not convertable to int: " +
282 raise ValueError(
"precision must be >= 0: " + `precision`)
293 n, p = self.
n, self.
p 294 i, f = divmod(abs(n),
_tento(p))
297 frac =
"0" * (p - len(frac)) + frac
305 return "FixedPoint" + `(str(self), self.
p)`
308 return _mkFP(self.
n, self.
p, type(self))
316 xn, yn, p =
_norm(self, other, FixedPoint=type(self))
320 """ Caution! == values must have equal hashes, and a FixedPoint 321 is essentially a rational in unnormalized form. There's 322 really no choice here but to normalize it, so hash is 323 potentially expensive. 324 n, p = self.__reduce() 326 Obscurity: if the value is an exact integer, p will be 0 now, 327 so the hash expression reduces to hash(n). So FixedPoints 328 that happen to be exact integers hash to the same things as 329 their int or long equivalents. This is Good. But if a 330 FixedPoint happens to have a value exactly representable as 331 a float, their hashes may differ. This is a teensy bit Bad. 334 return hash(n) ^ hash(p)
337 """ Returns true if this FixedPoint is not equal to zero""" 341 return _mkFP(-self.
n, self.
p, type(self))
344 """ Returns new FixedPoint containing the absolute value of this FixedPoint""" 351 n1, n2, p =
_norm(self, other, FixedPoint=type(self))
353 return _mkFP(n1 + n2, p, type(self))
358 if not isinstance(other, type(self)):
359 other = type(self)(other, self.
p)
363 return (-self) + other
366 n1, n2, p =
_norm(self, other, FixedPoint=type(self))
373 n1, n2, p =
_norm(self, other, FixedPoint=type(self))
375 raise ZeroDivisionError(
"FixedPoint division")
382 n1, n2, p =
_norm(self, other, FixedPoint=type(self))
383 return _mkFP(n2, p, FixedPoint=type(self)) / self
386 n1, n2, p =
_norm(self, other, FixedPoint=type(self))
388 raise ZeroDivisionError(
"FixedPoint modulo")
392 return q,
_mkFP(n1 - q * n2, p, type(self))
395 n1, n2, p =
_norm(self, other, FixedPoint=type(self))
396 return divmod(
_mkFP(n2, p), self)
402 n1, n2, p =
_norm(self, other, FixedPoint=type(self))
406 """Return the floating point representation of this FixedPoint. 407 Caution! float can lose precision. 410 return float(n) / float(
_tento(p))
413 """EJG/DF - Should this round instead? 414 Note e.g. long(-1.9) == -1L and long(1.9) == 1L in Python 415 Note that __int__ inherits whatever __long__ does, 416 and .frac() is affected too 418 answer = abs(self.
n) /
_tento(self.
p)
424 """Return integer value of FixedPoint object.""" 428 """Return fractional portion as a FixedPoint. 430 x.frac() + long(x) == x 432 return self - long(self)
437 return the result of rounding 438 Developers may substitute their own 'round' for custom rounding 442 n, leftover = divmod(x, y)
443 return self.round(x, y, n, leftover)
446 """ Return n, p s.t. self == n/10**p and n % 10 != 0""" 447 n, p = self.
n, self.
p 450 while p
and n % 10 == 0:
456 FixedPoint.round = bankersRounding
461 """Cached computation of 10**n""" 465 answer = cache[n] = 10L ** n
468 def _norm(x, y, isinstance=isinstance, FixedPoint=FixedPoint,
470 """Return xn, yn, p s.t. 475 x must be FixedPoint to begin with; if y is not FixedPoint, 476 it inherits its precision from x. 478 Note that this method is called a lot, so default-arg tricks are helpful. 480 assert isinstance(x, FixedPoint)
481 if not isinstance(y, FixedPoint):
495 def _mkFP(n, p, FixedPoint=FixedPoint):
496 """Make FixedPoint objext - Return a new FixedPoint object with the selected precision.""" 512 _parser = re.compile(
r""" 516 (?P<int>\d+) (\. (?P<frac>\d*))? 520 ([eE](?P<exp>[-+]? \d+))? 522 """, re.VERBOSE).match
528 """Return n, p s.t. float string value == n * 10**p exactly.""" 531 raise ValueError(
"can't parse as number: " + `s`)
539 intpart = m.group(
'int')
542 fracpart = m.group(
'onlyfrac')
544 fracpart = m.group(
'frac')
545 if fracpart
is None or fracpart ==
"":
550 i, f = long(intpart), long(fracpart)
551 nfrac = len(fracpart)
555 if m.group(
'sign') ==
"-":
561 """Unit testing framework""" 564 assert str(o) ==
"0.10" 566 assert str(t) ==
"-0.20000" 569 assert min(o, t) == min(t, o) == t
570 assert max(o, t) == max(t, o) == o
573 assert abs(t) > abs(o)
574 assert abs(o) < abs(t)
575 assert o == o
and t == t
577 assert o == -t/2 == -.5 * t
578 assert abs(t) == o + o
581 assert -(t/o) == (-t)/o == t/-o == 2
582 assert 1 + o == o + 1 == fp(
" +00.000011e+5 ")
584 assert o + t == t + o == -o
585 assert 2.0 * t == t * 2 ==
"2" * t == o/o * 2L * t
586 assert 1 - t == -(t - 1) == fp(6L)/5
587 assert t*t == 4*o*o == o*4*o == o*o*4
588 assert fp(2) -
"1" == 1
589 assert float(-1/t) == 5.0
591 assert 42 + fp(
"1e-20", p) - 42 == 0
592 assert 1/(42 + fp(
"1e-20", 20) - 42) == fp(
"100.0E18")
594 assert 1 - o == fp(
"5e-4", 10)
599 assert o == fp(
".998", 10)
602 assert o == fp(
".998", 10)
606 assert long(x) == -long(-x) == 1L
607 assert int(x) == -int(-x) == 1
608 assert x == long(x) + x.frac()
609 assert -x == long(-x) + (-x).frac()
610 assert fp(7) % 4 == 7 % fp(4) == 3
611 assert fp(-7) % 4 == -7 % fp(4) == 1
612 assert fp(-7) % -4 == -7 % fp(-4) == -3
613 assert fp(7.0) %
"-4.0" == 7 % fp(-4) == -1
614 assert fp(
"5.5") % fp(
"1.1") == fp(
"5.5e100") % fp(
"1.1e100") == 0
615 assert divmod(fp(
"1e100"), 3) == (long(fp(
"1e100")/3), 1)
617 if __name__ ==
'__main__':
def bankersRounding(self, dividend, divisor, quotient, remainder)
def _norm(x, y, isinstance=isinstance, FixedPoint=FixedPoint, _tento=_tento)
def __rmod__(self, other)
def __init__(self, value=0, precision=DEFAULT_PRECISION)
def _roundquotient(self, x, y)
def addHalfAndChop(self, dividend, divisor, quotient, remainder)
def __deepcopy__(self, memo)
def __rdivmod__(self, other)
def __rsub__(self, other)
def __rdiv__(self, other)
def __divmod__(self, other)
def set_precision(self, precision=DEFAULT_PRECISION)
def _mkFP(n, p, FixedPoint=FixedPoint)