1
2 __docformat__ = "plaintext"
3
4 """
5 FixedPoint objects support decimal arithmetic with a fixed number of
6 digits (called the object's precision) after the decimal point. The
7 number of digits before the decimal point is variable & unbounded.
8
9 The precision is user-settable on a per-object basis when a FixedPoint
10 is constructed, and may vary across FixedPoint objects. The precision
11 may also be changed after construction via FixedPoint.set_precision(p).
12 Note that if the precision of a FixedPoint is reduced via set_precision,
13 information may be lost to rounding.
14
15 >>> x = FixedPoint("5.55") # precision defaults to 2
16 >>> print x
17 5.55
18 >>> x.set_precision(1) # round to one fraction digit
19 >>> print x
20 5.6
21 >>> print FixedPoint("5.55", 1) # same thing setting to 1 in constructor
22 5.6
23 >>> repr(x) # returns constructor string that reproduces object exactly
24 "FixedPoint('5.6', 1)"
25 >>>
26
27 When FixedPoint objects of different precision are combined via + - * /,
28 the result is computed to the larger of the inputs' precisions, which also
29 becomes the precision of the resulting FixedPoint object.
30
31 >>> print FixedPoint("3.42") + FixedPoint("100.005", 3)
32 103.425
33 >>>
34
35 When a FixedPoint is combined with other numeric types (ints, floats,
36 strings representing a number) via + - * /, then similarly the computation
37 is carried out using-- and the result inherits --the FixedPoint's
38 precision.
39
40 >>> print FixedPoint(1) / 7
41 0.14
42 >>> print FixedPoint(1, 30) / 7
43 0.142857142857142857142857142857
44 >>>
45
46 The string produced by str(x) (implictly invoked by "print") always
47 contains at least one digit before the decimal point, followed by a
48 decimal point, followed by exactly x.get_precision() digits. If x is
49 negative, str(x)[0] == "-".
50
51 The FixedPoint constructor can be passed an int, long, string, float,
52 FixedPoint, or any object convertible to a float via float() or to a
53 long via long(). Passing a precision is optional; if specified, the
54 precision must be a non-negative int. There is no inherent limit on
55 the size of the precision, but if very very large you'll probably run
56 out of memory.
57
58 Note that conversion of floats to FixedPoint can be surprising, and
59 should be avoided whenever possible. Conversion from string is exact
60 (up to final rounding to the requested precision), so is greatly
61 preferred.
62
63 >>> print FixedPoint(1.1e30)
64 1099999999999999993725589651456.00
65 >>> print FixedPoint("1.1e30")
66 1100000000000000000000000000000.00
67 >>>
68
69 The following Python operators and functions accept FixedPoints in the
70 expected ways:
71
72 binary + - * / % divmod
73 with auto-coercion of other types to FixedPoint.
74 + - % divmod of FixedPoints are always exact.
75 * / of FixedPoints may lose information to rounding, in
76 which case the result is the infinitely precise answer
77 rounded to the result's precision.
78 divmod(x, y) returns (q, r) where q is a long equal to
79 floor(x/y) as if x/y were computed to infinite precision,
80 and r is a FixedPoint equal to x - q * y; no information
81 is lost. Note that q has the sign of y, and abs(r) < abs(y).
82 unary -
83 == != < > <= >= cmp
84 min max
85 float int long (int and long truncate)
86 abs
87 str repr
88 hash
89 use as dict keys
90 use as boolean (e.g. "if some_FixedPoint:" -- true iff not zero)
91
92 Methods unique to FixedPoints:
93 .copy() return new FixedPoint with same value
94 .frac() long(x) + x.frac() == x
95 .get_precision() return the precision(p) of this FixedPoint object
96 .set_precision(p) set the precision of this FixedPoint object
97
98 Provided as-is; use at your own risk; no warranty; no promises; enjoy!
99 """
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123 __copyright__ = "Copyright (C) Python Software Foundation"
124 __author__ = "Tim Peters"
125 __version__ = 0, 1, 0
126
128 """
129 rounding via nearest-even
130 increment the quotient if
131 the remainder is more than half of the divisor
132 or the remainder is exactly half the divisor and the quotient is odd
133 """
134 c = cmp(remainder << 1, divisor)
135
136 if c > 0 or (c == 0 and (quotient & 1) == 1):
137 quotient += 1
138 return quotient
139
141 """
142 the equivalent of 'add half and chop'
143 increment the quotient if
144 the remainder is greater than half of the divisor
145 or the remainder is exactly half the divisor and the quotient is >= 0
146 """
147 c = cmp(remainder << 1, divisor)
148
149 if c > 0 or (c == 0 and quotient >= 0):
150 quotient += 1
151 return quotient
152
153
154 try:
155 object
156 except NameError:
158 pass
160 return None
161
162
163
164 DEFAULT_PRECISION = 2
165
167 """Basic FixedPoint object class,
168 The exact value is self.n / 10**self.p;
169 self.n is a long; self.p is an int
170 """
171 __slots__ = ['n', 'p']
173 self.n = self.p = 0
174 self.set_precision(precision)
175 p = self.p
176
177 if isinstance(value, type("42.3e5")):
178 n, exp = _string2exact(value)
179
180 effective_exp = exp + p
181 if effective_exp > 0:
182 n = n * _tento(effective_exp)
183 elif effective_exp < 0:
184 n = self._roundquotient(n, _tento(-effective_exp))
185 self.n = n
186 return
187
188 if isinstance(value, type(42)) or isinstance(value, type(42L)):
189 self.n = long(value) * _tento(p)
190 return
191
192 if isinstance(value, type(self)):
193 temp = value.copy()
194 temp.set_precision(p)
195 self.n, self.p = temp.n, temp.p
196 return
197
198 if isinstance(value, type(42.0)):
199
200 import math
201 f, e = math.frexp(abs(value))
202 assert f == 0 or 0.5 <= f < 1.0
203
204
205
206
207
208 CHUNK = 28
209 top = 0L
210
211 while f:
212 f = math.ldexp(f, CHUNK)
213 digit = int(f)
214 assert digit >> CHUNK == 0
215 top = (top << CHUNK) | digit
216 f = f - digit
217 assert 0.0 <= f < 1.0
218 e = e - CHUNK
219
220
221
222
223 top = top * _tento(p)
224 if e >= 0:
225 n = top << e
226 else:
227 n = self._roundquotient(top, 1L << -e)
228 if value < 0:
229 n = -n
230 self.n = n
231 return
232
233 if isinstance(value, type(42-42j)):
234 raise TypeError("can't convert complex to FixedPoint: " +
235 `value`)
236
237
238 yes = 1
239 try:
240 asfloat = float(value)
241 except:
242 yes = 0
243 if yes:
244 self.__init__(asfloat, p)
245 return
246
247
248 yes = 1
249 try:
250 aslong = long(value)
251 except:
252 yes = 0
253 if yes:
254 self.__init__(aslong, p)
255 return
256
257 raise TypeError("can't convert to FixedPoint: " + `value`)
258
260 """Return the precision of this FixedPoint.
261
262 The precision is the number of decimal digits carried after
263 the decimal point, and is an int >= 0.
264 """
265
266 return self.p
267
269 """Change the precision carried by this FixedPoint to p.
270
271 precision must be an int >= 0, and defaults to
272 DEFAULT_PRECISION.
273
274 If precision is less than this FixedPoint's current precision,
275 information may be lost to rounding.
276 """
277
278 try:
279 p = int(precision)
280 except:
281 raise TypeError("precision not convertable to int: " +
282 `precision`)
283 if p < 0:
284 raise ValueError("precision must be >= 0: " + `precision`)
285
286 if p > self.p:
287 self.n = self.n * _tento(p - self.p)
288 elif p < self.p:
289 self.n = self._roundquotient(self.n, _tento(self.p - p))
290 self.p = p
291
292 precision = property(get_precision, set_precision)
293
295 n, p = self.n, self.p
296 i, f = divmod(abs(n), _tento(p))
297 if p:
298 frac = repr(f)[:-1]
299 frac = "0" * (p - len(frac)) + frac
300 else:
301 frac = ""
302 return "-"[:n<0] + \
303 repr(i)[:-1] + \
304 "." + frac
305
307 return "FixedPoint" + `(str(self), self.p)`
308
311
312 __copy__ = copy
313
316
320
322 """ Caution! == values must have equal hashes, and a FixedPoint
323 is essentially a rational in unnormalized form. There's
324 really no choice here but to normalize it, so hash is
325 potentially expensive.
326 n, p = self.__reduce()
327
328 Obscurity: if the value is an exact integer, p will be 0 now,
329 so the hash expression reduces to hash(n). So FixedPoints
330 that happen to be exact integers hash to the same things as
331 their int or long equivalents. This is Good. But if a
332 FixedPoint happens to have a value exactly representable as
333 a float, their hashes may differ. This is a teensy bit Bad.
334 """
335 n, p = self.__reduce()
336 return hash(n) ^ hash(p)
337
339 """ Returns true if this FixedPoint is not equal to zero"""
340 return self.n != 0
341
344
346 """ Returns new FixedPoint containing the absolute value of this FixedPoint"""
347 if self.n >= 0:
348 return self.copy()
349 else:
350 return -self
351
356
357 __radd__ = __add__
358
360 if not isinstance(other, type(self)):
361 other = type(self)(other, self.p)
362 return self.__add__(-other)
363
365 return (-self) + other
366
371
372 __rmul__ = __mul__
373
382
386
388 n1, n2, p = _norm(self, other, FixedPoint=type(self))
389 if n2 == 0:
390 raise ZeroDivisionError("FixedPoint modulo")
391
392 q = n1 / n2
393
394 return q, _mkFP(n1 - q * n2, p, type(self))
395
399
402
406
408 """Return the floating point representation of this FixedPoint.
409 Caution! float can lose precision.
410 """
411 n, p = self.__reduce()
412 return float(n) / float(_tento(p))
413
415 """EJG/DF - Should this round instead?
416 Note e.g. long(-1.9) == -1L and long(1.9) == 1L in Python
417 Note that __int__ inherits whatever __long__ does,
418 and .frac() is affected too
419 """
420 answer = abs(self.n) / _tento(self.p)
421 if self.n < 0:
422 answer = -answer
423 return answer
424
426 """Return integer value of FixedPoint object."""
427 return int(self.__long__())
428
430 """Return fractional portion as a FixedPoint.
431
432 x.frac() + long(x) == x
433 """
434 return self - long(self)
435
437 """
438 Divide x by y,
439 return the result of rounding
440 Developers may substitute their own 'round' for custom rounding
441 y must be > 0
442 """
443 assert y > 0
444 n, leftover = divmod(x, y)
445 return self.round(x, y, n, leftover)
446
448 """ Return n, p s.t. self == n/10**p and n % 10 != 0"""
449 n, p = self.n, self.p
450 if n == 0:
451 p = 0
452 while p and n % 10 == 0:
453 p = p - 1
454 n = n / 10
455 return n, p
456
457
458 FixedPoint.round = bankersRounding
459
460
461
463 """Cached computation of 10**n"""
464 try:
465 return cache[n]
466 except KeyError:
467 answer = cache[n] = 10L ** n
468 return answer
469
472 """Return xn, yn, p s.t.
473 p = max(x.p, y.p)
474 x = xn / 10**p
475 y = yn / 10**p
476
477 x must be FixedPoint to begin with; if y is not FixedPoint,
478 it inherits its precision from x.
479
480 Note that this method is called a lot, so default-arg tricks are helpful.
481 """
482 assert isinstance(x, FixedPoint)
483 if not isinstance(y, FixedPoint):
484 y = FixedPoint(y, x.p)
485 xn, yn = x.n, y.n
486 xp, yp = x.p, y.p
487 if xp > yp:
488 yn = yn * _tento(xp - yp)
489 p = xp
490 elif xp < yp:
491 xn = xn * _tento(yp - xp)
492 p = yp
493 else:
494 p = xp
495 return xn, yn, p
496
498 """Make FixedPoint objext - Return a new FixedPoint object with the selected precision."""
499 f = FixedPoint()
500
501 f.n = n
502 f.p = p
503 return f
504
505
506 import re
507
508
509
510
511
512
513
514 _parser = re.compile(r"""
515 \s*
516 (?P<sign>[-+])?
517 (
518 (?P<int>\d+) (\. (?P<frac>\d*))?
519 |
520 \. (?P<onlyfrac>\d+)
521 )
522 ([eE](?P<exp>[-+]? \d+))?
523 \s* $
524 """, re.VERBOSE).match
525
526 del re
527
528
530 """Return n, p s.t. float string value == n * 10**p exactly."""
531 m = _parser(s)
532 if m is None:
533 raise ValueError("can't parse as number: " + `s`)
534
535 exp = m.group('exp')
536 if exp is None:
537 exp = 0
538 else:
539 exp = int(exp)
540
541 intpart = m.group('int')
542 if intpart is None:
543 intpart = "0"
544 fracpart = m.group('onlyfrac')
545 else:
546 fracpart = m.group('frac')
547 if fracpart is None or fracpart == "":
548 fracpart = "0"
549 assert intpart
550 assert fracpart
551
552 i, f = long(intpart), long(fracpart)
553 nfrac = len(fracpart)
554 i = i * _tento(nfrac) + f
555 exp = exp - nfrac
556
557 if m.group('sign') == "-":
558 i = -i
559
560 return i, exp
561
563 """Unit testing framework"""
564 fp = FixedPoint
565 o = fp("0.1")
566 assert str(o) == "0.10"
567 t = fp("-20e-2", 5)
568 assert str(t) == "-0.20000"
569 assert t < o
570 assert o > t
571 assert min(o, t) == min(t, o) == t
572 assert max(o, t) == max(t, o) == o
573 assert o != t
574 assert --t == t
575 assert abs(t) > abs(o)
576 assert abs(o) < abs(t)
577 assert o == o and t == t
578 assert t.copy() == t
579 assert o == -t/2 == -.5 * t
580 assert abs(t) == o + o
581 assert abs(o) == o
582 assert o/t == -0.5
583 assert -(t/o) == (-t)/o == t/-o == 2
584 assert 1 + o == o + 1 == fp(" +00.000011e+5 ")
585 assert 1/o == 10
586 assert o + t == t + o == -o
587 assert 2.0 * t == t * 2 == "2" * t == o/o * 2L * t
588 assert 1 - t == -(t - 1) == fp(6L)/5
589 assert t*t == 4*o*o == o*4*o == o*o*4
590 assert fp(2) - "1" == 1
591 assert float(-1/t) == 5.0
592 for p in range(20):
593 assert 42 + fp("1e-20", p) - 42 == 0
594 assert 1/(42 + fp("1e-20", 20) - 42) == fp("100.0E18")
595 o = fp(".9995", 4)
596 assert 1 - o == fp("5e-4", 10)
597 o.set_precision(3)
598 assert o == 1
599 o = fp(".9985", 4)
600 o.set_precision(3)
601 assert o == fp(".998", 10)
602 assert o == o.frac()
603 o.set_precision(100)
604 assert o == fp(".998", 10)
605 o.set_precision(2)
606 assert o == 1
607 x = fp(1.99)
608 assert long(x) == -long(-x) == 1L
609 assert int(x) == -int(-x) == 1
610 assert x == long(x) + x.frac()
611 assert -x == long(-x) + (-x).frac()
612 assert fp(7) % 4 == 7 % fp(4) == 3
613 assert fp(-7) % 4 == -7 % fp(4) == 1
614 assert fp(-7) % -4 == -7 % fp(-4) == -3
615 assert fp(7.0) % "-4.0" == 7 % fp(-4) == -1
616 assert fp("5.5") % fp("1.1") == fp("5.5e100") % fp("1.1e100") == 0
617 assert divmod(fp("1e100"), 3) == (long(fp("1e100")/3), 1)
618
619 if __name__ == '__main__':
620 _test()
621