mpmath 0.3__zip → 0.4__zip

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,268 @@
1
+ """
2
+ Contents of this module:
3
+
4
+ * Integer and bit-level operations
5
+ * Miscellaneous utilities
6
+
7
+ """
8
+
9
+ import math
10
+ import decimal
11
+
12
+ # Same as standard Python float
13
+ STANDARD_PREC = 53
14
+
15
+ LOG2_10 = math.log(10,2) # 3.3219...
16
+
17
+
18
+ # All supported rounding modes. We define them as integer constants for easy
19
+ # management, but change __repr__ to give more information on inspection
20
+
21
+ class Rounding(int):
22
+
23
+ def __new__(cls, level, name):
24
+ a = int.__new__(cls, level)
25
+ a.name = name
26
+ return a
27
+
28
+ def __repr__(self):
29
+ return self.name
30
+
31
+ __str__ = __repr__
32
+
33
+ ROUND_DOWN = Rounding(1, 'ROUND_DOWN')
34
+ ROUND_UP = Rounding(2, 'ROUND_UP')
35
+ ROUND_FLOOR = Rounding(3, 'ROUND_FLOOR')
36
+ ROUND_CEILING = Rounding(4, 'ROUND_CEILING')
37
+ ROUND_HALF_UP = Rounding(5, 'ROUND_HALF_UP')
38
+ ROUND_HALF_DOWN = Rounding(6, 'ROUND_HALF_DOWN')
39
+ ROUND_HALF_EVEN = Rounding(7, 'ROUND_HALF_EVEN')
40
+
41
+
42
+ def giant_steps(start, target):
43
+ """Generate a list of precisions ranging from 'start' to 'target'
44
+ that doubles with each step. This is used by quadratically
45
+ convergent iterations (that is, Newton iterations), where we want
46
+ to keep the precision at the same level as the accuracy in each
47
+ step to minimize work.
48
+
49
+ For example, to find a sequence of precisions to reach 1000 bits
50
+ starting from a 53-bit estimate, giant_steps(53, 1000) gives
51
+
52
+ [64, 126, 251, 501, 1000]
53
+
54
+ So, if we start Newton's method with a 53-bit accurate initial
55
+ guess, the first iteration should be carried out at 64-bit
56
+ precision, the second at 126-bit precision, and so on.
57
+
58
+ Note the conservative rounding (1000 to 501, etc); this is used
59
+ guard against unit errors in the last place."""
60
+ L = [target]
61
+ while L[-1] > start*2:
62
+ L = L + [L[-1]//2 + 1]
63
+ return L[::-1]
64
+
65
+
66
+ def rshift_quick(x, n):
67
+ """For an integer x, calculate x >> n with the fastest (floor)
68
+ rounding. Unlike the plain Python expression (x >> n), n is
69
+ allowed to be negative, in which case a left shift is performed."""
70
+ if n >= 0: return x >> n
71
+ else: return x << (-n)
72
+
73
+
74
+ def lshift_quick(x, n):
75
+ """For an integer x, calculate x << n. Unlike the plain Python
76
+ expression (x << n), n is allowed to be negative, in which case a
77
+ right shift with default (floor) rounding is performed."""
78
+ if n >= 0: return x << n
79
+ else: return x >> (-n)
80
+
81
+
82
+ def make_fixed(s, prec):
83
+ """Convert a floating-point number to a fixed-point big integer"""
84
+ man, exp, bc = s
85
+ offset = exp + prec
86
+ if offset >= 0:
87
+ return man << offset
88
+ else:
89
+ return man >> (-offset)
90
+
91
+
92
+ def bitcount(n, log=math.log, table=(0,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4)):
93
+ """Give size of n in bits; i.e. the position of the highest set bit
94
+ in n. If n is negative, the absolute value is used. The bitcount of
95
+ zero is taken to be 0."""
96
+
97
+ if not n: return 0
98
+ if n < 0: n = -n
99
+
100
+ # math.log gives a good estimate, and never overflows, but
101
+ # is not always exact. Subtract 2 to underestimate, then
102
+ # count remaining bits by table lookup
103
+ bc = int(log(n, 2)) - 2
104
+ if bc < 0:
105
+ bc = 0
106
+ return bc + table[n >> bc]
107
+
108
+
109
+ # from decimal.py -- faster for small precs
110
+ def bitcount2(n, correction = {
111
+ '0': 4, '1': 3, '2': 2, '3': 2,
112
+ '4': 1, '5': 1, '6': 1, '7': 1,
113
+ '8': 0, '9': 0, 'a': 0, 'b': 0,
114
+ 'c': 0, 'd': 0, 'e': 0, 'f': 0}):
115
+ if n < 0:
116
+ n = -n
117
+ hex_n = "%x" % n
118
+ return 4*len(hex_n) - correction[hex_n[0]]
119
+
120
+
121
+ def trailing_zeros(n):
122
+ """Count trailing zero bits in an integer. If n is negative, it is
123
+ replaced by its absolute value."""
124
+ if n & 1: return 0
125
+ if not n: return 0
126
+ if n < 0: n = -n
127
+ t = 0
128
+ while not n & 0xffffffffffffffff: n >>= 64; t += 64
129
+ while not n & 0xff: n >>= 8; t += 8
130
+ while not n & 1: n >>= 1; t += 1
131
+ return t
132
+
133
+ #----------------------------------------------------------------------------
134
+ # Integer shifting with directed rounding
135
+ #
136
+
137
+ def round_floor(x, n):
138
+ if not n or not x: return x
139
+ if n < 0: return x << -n
140
+ return x >> n
141
+
142
+ def round_ceiling(x, n):
143
+ if not n or not x: return x
144
+ if n < 0: return x << -n
145
+ return -((-x) >> n)
146
+
147
+ def round_down(x, n):
148
+ if not n or not x: return x
149
+ if n < 0: return x << -n
150
+ if x > 0:
151
+ return x >> n
152
+ else:
153
+ return -((-x) >> n)
154
+
155
+ def round_up(x, n):
156
+ if not n or not x: return x
157
+ if n < 0: return x << -n
158
+ if x > 0:
159
+ return -((-x) >> n)
160
+ else:
161
+ return x >> n
162
+
163
+ def round_half_up(x, n):
164
+ if not n or not x: return x
165
+ if n < 0: return x << -n
166
+ positive = x > 0
167
+ if positive: t = x >> (n-1)
168
+ else: t = (-x) >> (n-1)
169
+ if t & 1:
170
+ if positive: return (t>>1)+1
171
+ else: return -((t>>1)+1)
172
+ if positive: return t>>1
173
+ else: return -(t>>1)
174
+
175
+ def round_half_down(x, n):
176
+ if not n or not x: return x
177
+ if n < 0: return x << -n
178
+ positive = x > 0
179
+ if positive: t = x >> (n-1)
180
+ else: t = (-x) >> (n-1)
181
+ if t & 1 and x & ((1<<(n-1))-1):
182
+ if positive: return (t>>1)+1
183
+ else: return -((t>>1)+1)
184
+ if positive: return t>>1
185
+ else: return -(t>>1)
186
+
187
+ def round_half_even(x, n):
188
+ if not n or not x: return x
189
+ if n < 0: return x << -n
190
+ positive = x > 0
191
+ if positive: t = x >> (n-1)
192
+ else: t = (-x) >> (n-1)
193
+ if t & 1 and (t&2 or x & ((1<<(n-1))-1)):
194
+ if positive: return (t>>1)+1
195
+ else: return -((t>>1)+1)
196
+ if positive: return t>>1
197
+ else: return -(t>>1)
198
+
199
+
200
+ rounding_functions = {
201
+ ROUND_FLOOR : round_floor,
202
+ ROUND_CEILING : round_ceiling,
203
+ ROUND_DOWN : round_down,
204
+ ROUND_UP : round_up,
205
+ ROUND_HALF_UP : round_half_up,
206
+ ROUND_HALF_DOWN : round_half_down,
207
+ ROUND_HALF_EVEN : round_half_even
208
+ }
209
+
210
+
211
+ def rshift(x, n, rounding):
212
+ """Shift x (a plain Python integer) n bits to the right (i.e.,
213
+ calculate x/(2**n)), and round to the nearest integer in accordance
214
+ with the specified rounding mode. The exponent n may be negative,
215
+ in which case x is shifted to the left (and no rounding is
216
+ necessary)."""
217
+ return rounding_functions[rounding](x, n)
218
+
219
+
220
+
221
+ # TODO: speed up for bases 2, 4, 8, 16, ...
222
+
223
+ def bin_to_radix(x, xbits, base, bdigits):
224
+ """
225
+ Radix conversion for fixed-point numbers. That is, convert
226
+ x * 2**xbits to floor(x * 10**bdigits).
227
+ """
228
+ return x * (base**bdigits) >> xbits
229
+
230
+ stddigits = '0123456789abcdefghijklmnopqrstuvwxyz'
231
+
232
+ def small_numeral(n, base=10, digits=stddigits):
233
+ """
234
+ Return the string numeral of a positive integer in an arbitrary
235
+ base. Most efficient for small input.
236
+ """
237
+ if base == 10:
238
+ return str(n)
239
+ digs = []
240
+ while n:
241
+ n, digit = divmod(n, base)
242
+ digs.append(digits[digit])
243
+ return "".join(digs[::-1])
244
+
245
+ def numeral(n, base=10, size=0, digits=stddigits):
246
+ """
247
+ Represent the integer n as a string of digits in the given base.
248
+ Recursive division is used to make this function about 3x faster
249
+ than Python's str() for converting integers to decimal strings.
250
+
251
+ The 'size' parameters specifies the number of digits in n; this
252
+ number is only used to determine splitting points and need not
253
+ be exact.
254
+ """
255
+
256
+ if n < 0:
257
+ return "-" + numeral(-n, base, size, digits)
258
+
259
+ # Fast enough to do directly
260
+ if size < 250:
261
+ return small_numeral(n, base, digits)
262
+
263
+ # Divide in half
264
+ half = (size // 2) + (size & 1)
265
+ A, B = divmod(n, base**half)
266
+ ad = numeral(A, base, half, digits)
267
+ bd = numeral(B, base, half, digits).rjust(half, "0")
268
+ return ad + bd
@@ -1,17 +1,44 @@
1
1
  from lib import *
2
2
  from decimal import Decimal
3
3
 
4
+
4
5
  class mpnumeric(object):
5
6
  """Base class for mpf and mpc. Calling mpnumeric(x) returns an mpf
6
7
  if x can be converted to an mpf (if it is a float, int, mpf, ...),
7
8
  and an mpc if x is complex."""
9
+
8
10
  def __new__(cls, val):
11
+ # TODO: should maybe normalize here
9
12
  if isinstance(val, cls):
10
13
  return val
11
14
  if isinstance(val, complex):
12
15
  return mpc(val)
13
16
  return mpf(val)
14
17
 
18
+ def convert_lossless(x):
19
+ """Attempt to convert x to an mpf or mpc losslessly. If x is an
20
+ mpf or mpc, return it unchanged. If x is an int, create an mpf with
21
+ sufficient precision to represent it exactly.
22
+
23
+ If x is a decimal or str, just convert it to an mpf with the
24
+ current working precision (perhaps this should be done
25
+ differently...)"""
26
+ if isinstance(x, mpnumeric):
27
+ return x
28
+ if isinstance(x, float):
29
+ return make_mpf(from_float(x, 53, ROUND_FLOOR))
30
+ if isinstance(x, int_types):
31
+ return make_mpf(from_int(x, bitcount(x), ROUND_FLOOR))
32
+ if isinstance(x, complex):
33
+ return mpc(x)
34
+ if isinstance(x, (Decimal, str)):
35
+ if x == 'inf': return inf
36
+ if x == '-inf': return minus_inf
37
+ if x == 'nan': return nan
38
+ return make_mpf(from_str(x, mpf._prec, mpf._rounding))
39
+ raise TypeError("cannot create mpf from " + repr(x))
40
+
41
+
15
42
  class context(type):
16
43
  """Metaclass for mpf and mpc. Holds global working precision."""
17
44
 
@@ -44,14 +71,15 @@ class context(type):
44
71
 
45
72
  int_types = (int, long)
46
73
 
74
+
47
75
  def _convert(x):
48
76
  """Convet x to mpf data"""
49
77
  if isinstance(x, float):
50
- return float_from_pyfloat(x, mpf._prec, mpf._rounding)
78
+ return from_float(x, mpf._prec, mpf._rounding)
51
79
  if isinstance(x, int_types):
52
- return float_from_int(x, mpf._prec, mpf._rounding)
80
+ return from_int(x, mpf._prec, mpf._rounding)
53
81
  if isinstance(x, (Decimal, str)):
54
- return decimal_to_binary(x, mpf._prec, mpf._rounding)
82
+ return from_str(x, mpf._prec, mpf._rounding)
55
83
  raise TypeError("cannot create mpf from " + repr(x))
56
84
 
57
85
 
@@ -93,7 +121,7 @@ class mpf(mpnumeric):
93
121
  """A new mpf can be created from a Python float, an int, a
94
122
  Decimal, or a decimal string representing a number in
95
123
  floating-point format. Examples:
96
-
124
+
97
125
  mpf(25)
98
126
  mpf(2.5)
99
127
  mpf('2.5')
@@ -120,10 +148,10 @@ class mpf(mpnumeric):
120
148
 
121
149
  def __repr__(s):
122
150
  st = "mpf('%s')"
123
- return st % binary_to_decimal(s.val, mpf._dps+2)
151
+ return st % to_str(s.val, mpf._dps+2)
124
152
 
125
153
  def __str__(s):
126
- return binary_to_decimal(s.val, mpf._dps)
154
+ return to_str(s.val, mpf._dps)
127
155
 
128
156
  def __hash__(s):
129
157
  try:
@@ -136,14 +164,17 @@ class mpf(mpnumeric):
136
164
  return hash(self.val)
137
165
 
138
166
  def __int__(s):
139
- return float_to_int(s.val)
167
+ return to_int(s.val)
140
168
 
141
169
  def __float__(s):
142
- return float_to_pyfloat(s.val)
170
+ return to_float(s.val)
143
171
 
144
172
  def __complex__(s):
145
173
  return float(s) + 0j
146
174
 
175
+ def __nonzero__(s):
176
+ return bool(s.man)
177
+
147
178
  def __eq__(s, t):
148
179
  if not isinstance(t, mpf):
149
180
  if isinstance(t, complex_types):
@@ -256,7 +287,7 @@ class mpf(mpnumeric):
256
287
  return power(s, t)
257
288
 
258
289
  def __rpow__(s, t):
259
- return mpnumeric(t) ** s
290
+ return convert_lossless(t) ** s
260
291
 
261
292
  def sqrt(s):
262
293
  return sqrt(s)
@@ -307,7 +338,6 @@ def make_mpf(tpl, construct=object.__new__, cls=mpf):
307
338
  return a
308
339
 
309
340
 
310
-
311
341
  class mpc(mpnumeric):
312
342
  """An mpc represents a complex number using a pair of mpf:s (one
313
343
  for the real part and another for the imaginary part.) The mpc
@@ -380,11 +410,8 @@ class mpc(mpnumeric):
380
410
  def __mul__(s, t):
381
411
  if not isinstance(t, mpc):
382
412
  t = mpc(t)
383
- a = s.real; b = s.imag; c = t.real; d = t.imag
384
- if b == d == 0:
385
- return mpc(a*c, 0)
386
- else:
387
- return mpc(a*c-b*d, a*d+b*c)
413
+ return mpc(*fcmul(s.real.val, s.imag.val, t.real.val, t.imag.val,
414
+ mpf._prec, mpf._rounding))
388
415
 
389
416
  __rmul__ = __mul__
390
417
 
@@ -418,7 +445,7 @@ class mpc(mpnumeric):
418
445
  return power(s, n)
419
446
 
420
447
  def __rpow__(s, t):
421
- return mpnumeric(t) ** s
448
+ return convert_lossless(t) ** s
422
449
 
423
450
  # TODO: refactor and merge with mpf.ae
424
451
  def ae(s, t, rel_eps=None, abs_eps=None):
@@ -472,7 +499,10 @@ class constant(mpf):
472
499
  # return "<%s: %s~>" % (self.name, mpf.__str__(self))
473
500
 
474
501
 
502
+ _180 = from_int(180, 10, ROUND_FLOOR)
503
+
475
504
  pi = constant(fpi, "pi")
505
+ degree = constant(lambda p, r: fdiv(fpi(p+4, ROUND_FLOOR), _180, p, r), "degree")
476
506
  e = constant(lambda p, r: fexp(fone, p, r), "e")
477
507
  cgamma = constant(fgamma, "Euler's constant gamma")
478
508
  clog2 = constant(flog2, "log(2)")
@@ -483,7 +513,7 @@ def sqrt(x):
483
513
  """For real x >= 0, returns the square root of x. For negative or
484
514
  complex x, returns the principal branch of the complex square root
485
515
  of x."""
486
- x = mpnumeric(x)
516
+ x = convert_lossless(x)
487
517
  if isinstance(x, mpf) and x.val[0] >= 0:
488
518
  return make_mpf(fsqrt(x.val, mpf._prec, mpf._rounding))
489
519
  x = mpc(x)
@@ -500,7 +530,7 @@ def hypot(x, y):
500
530
  # can construct all of them the same way (unlike log, sqrt, etc)
501
531
  def ef(name, real_f, complex_f, doc):
502
532
  def f(x):
503
- x = mpnumeric(x)
533
+ x = convert_lossless(x)
504
534
  if isinstance(x, mpf):
505
535
  return make_mpf(real_f(x.val, mpf._prec, mpf._rounding))
506
536
  else:
@@ -519,7 +549,7 @@ sinh = ef('sinh', fsinh, fcsinh, "Returns the hyperbolic sine of x.")
519
549
  # TODO: implement tanh and complex tan in lib instead
520
550
  def tan(x):
521
551
  """Returns the tangent of x."""
522
- x = mpnumeric(x)
552
+ x = convert_lossless(x)
523
553
  if isinstance(x, mpf):
524
554
  return make_mpf(ftan(x.val, mpf._prec, mpf._rounding))
525
555
  # the complex division can cause enormous cancellation.
@@ -531,7 +561,7 @@ def tan(x):
531
561
 
532
562
  def tanh(x):
533
563
  """Returns the hyperbolic tangent of x."""
534
- x = mpnumeric(x)
564
+ x = convert_lossless(x)
535
565
  oldprec = mpf._prec
536
566
  a = abs(x)
537
567
  mpf._prec += 10
@@ -569,7 +599,7 @@ def log(x, b=None):
569
599
  a = log(x) / log(b)
570
600
  mpf.prec -= 3
571
601
  return +a
572
- x = mpnumeric(x)
602
+ x = convert_lossless(x)
573
603
  if not x:
574
604
  raise ValueError, "logarithm of 0"
575
605
  if isinstance(x, mpf) and x.val[0] > 0:
@@ -587,7 +617,7 @@ def power(x, y):
587
617
 
588
618
  def atan(x):
589
619
  """Returns the inverse tangent of x."""
590
- x = mpnumeric(x)
620
+ x = convert_lossless(x)
591
621
  if isinstance(x, mpf):
592
622
  return make_mpf(fatan(x.val, mpf._prec, mpf._rounding))
593
623
  # TODO: maybe get this to agree with Python's cmath atan about the
@@ -631,10 +661,8 @@ def _asin_complex(z):
631
661
  def asin(x):
632
662
  """Returns the inverse sine of x. Outside the range [-1, 1], the
633
663
  result is complex and defined as the principal branch value of
634
-
635
- -i * log(i * x + sqrt(1 - x**2)).
636
- """
637
- x = mpnumeric(x)
664
+ -i * log(i * x + sqrt(1 - x**2))."""
665
+ x = convert_lossless(x)
638
666
  if isinstance(x, mpf) and abs(x) <= 1:
639
667
  return _asin_complex(x).real
640
668
  return _asin_complex(x)
@@ -648,21 +676,16 @@ def _acos_complex(z):
648
676
  def acos(x):
649
677
  """Returns the inverse cosine of x. Outside the range [-1, 1], the
650
678
  result is complex and defined as the principal branch value of
651
-
652
- pi/2 + i * log(i * x + sqrt(1 - x**2)).
653
- """
654
- x = mpnumeric(x)
679
+ pi/2 + i * log(i * x + sqrt(1 - x**2))."""
680
+ x = convert_lossless(x)
655
681
  if isinstance(x, mpf) and abs(x) <= 1:
656
682
  return _acos_complex(x).real
657
683
  return _acos_complex(x)
658
684
 
659
685
  def asinh(x):
660
686
  """Returns the inverse hyperbolic sine of x. For complex x, the
661
- result is the principal branch value of
662
-
663
- log(x + sqrt(1 + x**2)).
664
- """
665
- x = mpnumeric(x)
687
+ result is the principal branch value of log(x + sqrt(1 + x**2))."""
688
+ x = convert_lossless(x)
666
689
  oldprec = mpf._prec
667
690
  a = abs(x)
668
691
  mpf._prec += 10
@@ -679,7 +702,7 @@ def acosh(x):
679
702
  """Returns the inverse hyperbolic cosine of x. The value is
680
703
  given by log(x + sqrt(1 + x**2)), where the principal branch is
681
704
  used when the result is complex."""
682
- x = mpnumeric(x)
705
+ x = convert_lossless(x)
683
706
  mpf._prec += 10
684
707
  t = log(x + sqrt(x-1)*sqrt(x+1))
685
708
  mpf._prec -= 10
@@ -688,11 +711,8 @@ def acosh(x):
688
711
  def atanh(x):
689
712
  """Returns the inverse hyperbolic tangent of x. Outside the range
690
713
  [-1, 1], the result is complex and defined as the principal branch
691
- value of
692
-
693
- (log(1+x) - log(1-x))/2.
694
- """
695
- x = mpnumeric(x)
714
+ value of (log(1+x) - log(1-x))/2."""
715
+ x = convert_lossless(x)
696
716
  oldprec = mpf._prec
697
717
  a = abs(x)
698
718
  mpf._prec += 10
@@ -706,8 +726,14 @@ def atanh(x):
706
726
  mpf._prec = oldprec
707
727
  return +t
708
728
 
729
+ def rand():
730
+ """Return an mpf chosen randomly from [0, 1)."""
731
+ return make_mpf(frand(mpf._prec))
732
+
733
+
734
+ __all__ = ["mpnumeric", "mpf", "mpc", "pi", "e", "cgamma", "clog2", "clog10",
735
+ "j", "sqrt", "hypot", "exp", "log", "cos", "sin", "tan", "atan", "atan2",
736
+ "power", "asin", "acos", "sinh", "cosh", "tanh", "asinh", "acosh", "atanh",
737
+ "arg", "degree", "rand"]
709
738
 
710
- __all__ = ["mpnumeric", "mpf", "mpc", "pi", "e", "cgamma", "clog2", "clog10", "j",
711
- "sqrt", "hypot", "exp", "log", "cos", "sin", "tan", "atan", "atan2", "power",
712
- "asin", "acos", "sinh", "cosh", "tanh", "asinh", "acosh", "atanh", "arg"]
713
739
 
@@ -2,10 +2,10 @@ from distutils.core import setup
2
2
 
3
3
  setup(name='mpmath',
4
4
  description = 'Python library for arbitrary-precision floating-point arithmetic',
5
- version='0.3',
5
+ version='0.4',
6
6
  url='http://mpmath.googlecode.com',
7
7
  author='Fredrik Johansson',
8
8
  author_email='fredrik.johansson@gmail.com',
9
9
  license = 'BSD',
10
- packages=['mpmath'],
10
+ packages=['mpmath', 'mpmath/lib'],
11
11
  )
@@ -0,0 +1,139 @@
1
+ from mpmath import *
2
+ from mpmath.lib import *
3
+ from decimal import getcontext, Decimal
4
+ import dmath
5
+ from random import seed, random, randint
6
+
7
+ def getrandom(type):
8
+ if type == 'mpf':
9
+ return mpf(random() * 2.0**randint(-10, 10)) ** 0.5
10
+ if type == 'mpfval':
11
+ return (mpf(random() * 2.0**randint(-10, 10)) ** 0.5).val
12
+ if type == 'Decimal':
13
+ return Decimal(repr(random() * 2.0**randint(-10, 10))).sqrt()
14
+ raise TypeError
15
+
16
+ def rndnums(type, N):
17
+ seed(1234)
18
+ xs = [getrandom(type) for i in xrange(N)]
19
+ ys = xs[::-1]
20
+ xys = zip(xs, ys)
21
+ return xs, ys, xys
22
+
23
+ def setprec(type, prec):
24
+ if type == 'Decimal':
25
+ getcontext().prec = prec
26
+ else:
27
+ mpf.dps = prec
28
+ # change prec value to bits for mpfval use
29
+ prec = mpf.prec
30
+ return prec
31
+
32
+ testcode = \
33
+ """
34
+ def testit(prec, N):
35
+ from time import clock
36
+ RF = ROUND_HALF_EVEN
37
+ prec = setprec('TYPE', prec)
38
+ xs, ys, xys = rndnums('TYPE', N)
39
+ t = 1e100
40
+ for i in range(3):
41
+ t1 = clock()
42
+ for x, y in xys:
43
+ OP
44
+ t2 = clock()
45
+ t = min(t, t2-t1)
46
+ return t
47
+ """
48
+
49
+ tests = []
50
+ atests = [
51
+ ('Convert to integer (int(x))', 'int(x)', 'int(x)', 'to_int(x)'),
52
+ ('Convert to string (str(x))', 'str(x)', 'str(x)', 'to_str(x, int(prec/3.321))'),
53
+ ('Convert to float (float(x))', 'float(x)', 'float(x)', 'to_float(x)'),
54
+ ('Equality (x==y)', 'x==y', 'x==y', 'feq(x, y)'),
55
+ ('Comparison (x<y)', 'x<y', 'x<y', 'fcmp(x, y) < 0'),
56
+ ('Addition (x+y)', 'x+y', 'x+y', 'fadd(x, y, prec, RF)'),
57
+ ('Subtraction (x-y)', 'x+y', 'x+y', 'fsub(x, y, prec, RF)'),
58
+ ('Multiplication (x*y)', 'x*y', 'x*y', 'fmul(x, y, prec, RF)'),
59
+ ('Division (x/y)', 'x/y', 'x/y', 'fdiv(x, y, prec, RF)'),
60
+ ('Square root (x^0.5)', 'x.sqrt()', 'sqrt(x)', 'fsqrt(x, prec, RF)'),
61
+ ('Integer power (x^42)', 'x**42', 'x**42', 'fpow(x, 42, prec, RF)'),
62
+ ('Exponential function (exp(x))', 'dmath.exp(x)', 'exp(x)', 'fexp(x, prec, RF)'),
63
+ ('Natural logarithm (log(x))', 'dmath.log(x+1)', 'log(x)', 'flog(x, prec, RF)'),
64
+ ('Sine (sin(x))', 'dmath.sin(x)', 'sin(x)', 'fsin(x, prec, RF)'),
65
+ ('Tangent (tan(x))', 'dmath.tan(x)', 'tan(x)', 'ftan(x, prec, RF)'),
66
+ ('Inverse tangent(atan(x))', 'dmath.atan(x)', 'atan(x)', 'fatan(x, prec, RF)'),
67
+ ('Hyperbolic cosine (cosh(x))', 'dmath.cosh(x)', 'cosh(x)', 'fcosh(x, prec, RF)')
68
+ ]
69
+
70
+ slow = ["exp", "log", "sin", "tan", "cos"]
71
+
72
+ for op in atests:
73
+ cases = [op[0]]
74
+ if op[1]:
75
+ exec testcode.replace("OP", op[1]).replace("TYPE", "Decimal")
76
+ cases += [testit]
77
+ else:
78
+ cases += [None]
79
+ exec testcode.replace("OP", op[2]).replace("TYPE", "mpf")
80
+ cases += [testit]
81
+ exec testcode.replace("OP", op[3]).replace("TYPE", "mpfval")
82
+ cases += [testit]
83
+ tests.append(cases)
84
+
85
+ def rd(x):
86
+ if x > 100000: return int(x // 10000) * 10000
87
+ if x > 10000: return int(x // 1000) * 1000
88
+ if x > 1000: return int(x // 100) * 100
89
+ if x > 100: return int(x // 10) * 10
90
+ return int(x)
91
+
92
+ def runtests():
93
+ results = []
94
+ for test in tests:
95
+ name, dectest, mpftest, mpfvaltest = test
96
+ if any(s in name for s in slow):
97
+ N = 1
98
+ precs = [15, 30, 100, 300]
99
+ else:
100
+ N = 10
101
+ precs = [15, 30, 100, 300, 1000]
102
+ header_name = "*" + name + "*"
103
+ rows = []
104
+ for prec in precs:
105
+ print name, prec
106
+ if dectest is None:
107
+ t1 = 1e1000-1e1000
108
+ else:
109
+ t1 = dectest(prec, N)
110
+ t2 = mpftest(prec, N)
111
+ t3 = mpfvaltest(prec, N)
112
+ s = []
113
+ s += ["%i" % prec]
114
+ s += [str(rd(N/t1))]
115
+ s += [str(rd(N/t2)) + (" (%.1fx)" % (t1/t2))]
116
+ s += [str(rd(N/t3)) + (" (%.1fx)" % (t1/t3))]
117
+ rows.append(s)
118
+ results.append((header_name, rows))
119
+ return results
120
+
121
+ import gc
122
+ gc.disable()
123
+ results1 = runtests()
124
+ import psyco
125
+ psyco.full()
126
+ results2 = runtests()
127
+
128
+ import sys
129
+ sys.stdout = open("results.txt", "w")
130
+
131
+ for r1, r2 in zip(results1, results2):
132
+ name = r1[0]
133
+ print name
134
+ print "|| *digits* || *Decimal* || *mpf* || *raw mpf* || *Decimal+psyco* || *mpf+psyco* || *raw mpf+psyco* ||"
135
+ for a, b in zip(r1[1], r2[1]):
136
+ cols = a + b[1:]
137
+ print "|| " + (" || ".join(cols)) + " ||"
138
+ print
139
+