1
-
2
1
# Python test set -- math module
3
2
# XXXX Should not do tests around zero only
4
3
19
18
NAN = float ('nan' )
20
19
INF = float ('inf' )
21
20
NINF = float ('-inf' )
22
-
23
21
FLOAT_MAX = sys .float_info .max
24
22
FLOAT_MIN = sys .float_info .min
25
23
@@ -43,8 +41,10 @@ def to_ulps(x):
43
41
adjacent floats are converted to adjacent integers. Then
44
42
abs(ulps(x) - ulps(y)) gives the difference in ulps between two
45
43
floats.
44
+
46
45
The results from this function will only make sense on platforms
47
46
where native doubles are represented in IEEE 754 binary64 format.
47
+
48
48
Note: 0.0 and -0.0 are converted to 0 and -1, respectively.
49
49
"""
50
50
n = struct .unpack ('<q' , struct .pack ('<d' , x ))[0 ]
@@ -81,6 +81,7 @@ def count_set_bits(n):
81
81
def partial_product (start , stop ):
82
82
"""Product of integers in range(start, stop, 2), computed recursively.
83
83
start and stop should both be odd, with start <= stop.
84
+
84
85
"""
85
86
numfactors = (stop - start ) >> 1
86
87
if not numfactors :
@@ -94,6 +95,7 @@ def partial_product(start, stop):
94
95
def py_factorial (n ):
95
96
"""Factorial of nonnegative integer n, via "Binary Split Factorial Formula"
96
97
described at http://www.luschny.de/math/factorial/binarysplitfact.html
98
+
97
99
"""
98
100
inner = outer = 1
99
101
for i in reversed (range (n .bit_length ())):
@@ -105,6 +107,7 @@ def ulp_abs_check(expected, got, ulp_tol, abs_tol):
105
107
"""Given finite floats `expected` and `got`, check that they're
106
108
approximately equal to within the given number of ulps or the
107
109
given absolute tolerance, whichever is bigger.
110
+
108
111
Returns None on success and an error message on failure.
109
112
"""
110
113
ulp_error = abs (to_ulps (expected ) - to_ulps (got ))
@@ -120,12 +123,14 @@ def ulp_abs_check(expected, got, ulp_tol, abs_tol):
120
123
121
124
def parse_mtestfile (fname ):
122
125
"""Parse a file with test values
126
+
123
127
-- starts a comment
124
128
blank lines, or lines containing only a comment, are ignored
125
129
other lines are expected to have the form
126
130
id fn arg -> expected [flag]*
131
+
127
132
"""
128
- with open (fname ) as fp :
133
+ with open (fname , encoding = "utf-8" ) as fp :
129
134
for line in fp :
130
135
# strip comments, and skip blank lines
131
136
if '--' in line :
@@ -144,10 +149,11 @@ def parse_mtestfile(fname):
144
149
145
150
def parse_testfile (fname ):
146
151
"""Parse a file with test values
152
+
147
153
Empty lines or lines starting with -- are ignored
148
154
yields id, fn, arg_real, arg_imag, exp_real, exp_imag
149
155
"""
150
- with open (fname ) as fp :
156
+ with open (fname , encoding = "utf-8" ) as fp :
151
157
for line in fp :
152
158
# skip comment lines and blank lines
153
159
if line .startswith ('--' ) or not line .strip ():
@@ -170,9 +176,11 @@ def result_check(expected, got, ulp_tol=5, abs_tol=0.0):
170
176
"""Compare arguments expected and got, as floats, if either
171
177
is a float, using a tolerance expressed in multiples of
172
178
ulp(expected) or absolutely (if given and greater).
179
+
173
180
As a convenience, when neither argument is a float, and for
174
181
non-finite floats, exact equality is demanded. Also, nan==nan
175
182
as far as this function is concerned.
183
+
176
184
Returns None on success and an error message on failure.
177
185
"""
178
186
@@ -232,6 +240,7 @@ def ftest(self, name, got, expected, ulp_tol=5, abs_tol=0.0):
232
240
"""Compare arguments expected and got, as floats, if either
233
241
is a float, using a tolerance expressed in multiples of
234
242
ulp(expected) or absolutely, whichever is greater.
243
+
235
244
As a convenience, when neither argument is a float, and for
236
245
non-finite floats, exact equality is demanded. Also, nan==nan
237
246
in this function.
@@ -492,17 +501,11 @@ def testFactorial(self):
492
501
self .assertRaises (ValueError , math .factorial , - 1 )
493
502
self .assertRaises (ValueError , math .factorial , - 10 ** 100 )
494
503
495
- # TODO: RUSTPYTHON
496
- @unittest .expectedFailure
497
504
def testFactorialNonIntegers (self ):
498
- with self .assertWarns (DeprecationWarning ):
499
- self .assertEqual (math .factorial (5.0 ), 120 )
500
- with self .assertWarns (DeprecationWarning ):
501
- self .assertRaises (ValueError , math .factorial , 5.2 )
502
- with self .assertWarns (DeprecationWarning ):
503
- self .assertRaises (ValueError , math .factorial , - 1.0 )
504
- with self .assertWarns (DeprecationWarning ):
505
- self .assertRaises (ValueError , math .factorial , - 1e100 )
505
+ self .assertRaises (TypeError , math .factorial , 5.0 )
506
+ self .assertRaises (TypeError , math .factorial , 5.2 )
507
+ self .assertRaises (TypeError , math .factorial , - 1.0 )
508
+ self .assertRaises (TypeError , math .factorial , - 1e100 )
506
509
self .assertRaises (TypeError , math .factorial , decimal .Decimal ('5' ))
507
510
self .assertRaises (TypeError , math .factorial , decimal .Decimal ('5.2' ))
508
511
self .assertRaises (TypeError , math .factorial , "5" )
@@ -513,8 +516,7 @@ def testFactorialHugeInputs(self):
513
516
# Currently raises OverflowError for inputs that are too large
514
517
# to fit into a C long.
515
518
self .assertRaises (OverflowError , math .factorial , 10 ** 100 )
516
- with self .assertWarns (DeprecationWarning ):
517
- self .assertRaises (OverflowError , math .factorial , 1e100 )
519
+ self .assertRaises (TypeError , math .factorial , 1e100 )
518
520
519
521
def testFloor (self ):
520
522
self .assertRaises (TypeError , math .floor )
@@ -587,7 +589,6 @@ def testfrexp(name, result, expected):
587
589
self .assertEqual (math .frexp (NINF )[0 ], NINF )
588
590
self .assertTrue (math .isnan (math .frexp (NAN )[0 ]))
589
591
590
-
591
592
@requires_IEEE_754
592
593
@unittest .skipIf (HAVE_DOUBLE_ROUNDING ,
593
594
"fsum is not exact on machines with double rounding" )
@@ -611,6 +612,7 @@ def msum(iterable):
611
612
"""Full precision summation. Compute sum(iterable) without any
612
613
intermediate accumulation of error. Based on the 'lsum' function
613
614
at http://code.activestate.com/recipes/393090/
615
+
614
616
"""
615
617
tmant , texp = 0 , 0
616
618
for x in iterable :
@@ -684,8 +686,6 @@ def msum(iterable):
684
686
s = msum (vals )
685
687
self .assertEqual (msum (vals ), math .fsum (vals ))
686
688
687
-
688
- # Python 3.9
689
689
def testGcd (self ):
690
690
gcd = math .gcd
691
691
self .assertEqual (gcd (0 , 0 ), 0 )
@@ -726,7 +726,7 @@ def testGcd(self):
726
726
self .assertRaises (TypeError , gcd , 120.0 , 84 )
727
727
self .assertRaises (TypeError , gcd , 120 , 84.0 )
728
728
self .assertRaises (TypeError , gcd , 120 , 1 , 84.0 )
729
- # self.assertEqual(gcd(MyIndexable(120), MyIndexable(84)), 12) # TODO: RUSTPYTHON
729
+ self .assertEqual (gcd (MyIndexable (120 ), MyIndexable (84 )), 12 )
730
730
731
731
def testHypot (self ):
732
732
from decimal import Decimal
@@ -795,13 +795,79 @@ def testHypot(self):
795
795
# Verify scaling for extremely large values
796
796
fourthmax = FLOAT_MAX / 4.0
797
797
for n in range (32 ):
798
- self .assertEqual (hypot (* ([fourthmax ]* n )), fourthmax * math .sqrt (n ))
798
+ self .assertTrue (math .isclose (hypot (* ([fourthmax ]* n )),
799
+ fourthmax * math .sqrt (n )))
799
800
800
801
# Verify scaling for extremely small values
801
802
for exp in range (32 ):
802
803
scale = FLOAT_MIN / 2.0 ** exp
803
804
self .assertEqual (math .hypot (4 * scale , 3 * scale ), 5 * scale )
804
805
806
+ @requires_IEEE_754
807
+ @unittest .skipIf (HAVE_DOUBLE_ROUNDING ,
808
+ "hypot() loses accuracy on machines with double rounding" )
809
+ # TODO: RUSTPYTHON
810
+ @unittest .expectedFailure
811
+ def testHypotAccuracy (self ):
812
+ # Verify improved accuracy in cases that were known to be inaccurate.
813
+ #
814
+ # The new algorithm's accuracy depends on IEEE 754 arithmetic
815
+ # guarantees, on having the usual ROUND HALF EVEN rounding mode, on
816
+ # the system not having double rounding due to extended precision,
817
+ # and on the compiler maintaining the specified order of operations.
818
+ #
819
+ # This test is known to succeed on most of our builds. If it fails
820
+ # some build, we either need to add another skipIf if the cause is
821
+ # identifiable; otherwise, we can remove this test entirely.
822
+
823
+ hypot = math .hypot
824
+ Decimal = decimal .Decimal
825
+ high_precision = decimal .Context (prec = 500 )
826
+
827
+ for hx , hy in [
828
+ # Cases with a 1 ulp error in Python 3.7 compiled with Clang
829
+ ('0x1.10e89518dca48p+29' , '0x1.1970f7565b7efp+30' ),
830
+ ('0x1.10106eb4b44a2p+29' , '0x1.ef0596cdc97f8p+29' ),
831
+ ('0x1.459c058e20bb7p+30' , '0x1.993ca009b9178p+29' ),
832
+ ('0x1.378371ae67c0cp+30' , '0x1.fbe6619854b4cp+29' ),
833
+ ('0x1.f4cd0574fb97ap+29' , '0x1.50fe31669340ep+30' ),
834
+ ('0x1.494b2cdd3d446p+29' , '0x1.212a5367b4c7cp+29' ),
835
+ ('0x1.f84e649f1e46dp+29' , '0x1.1fa56bef8eec4p+30' ),
836
+ ('0x1.2e817edd3d6fap+30' , '0x1.eb0814f1e9602p+29' ),
837
+ ('0x1.0d3a6e3d04245p+29' , '0x1.32a62fea52352p+30' ),
838
+ ('0x1.888e19611bfc5p+29' , '0x1.52b8e70b24353p+29' ),
839
+
840
+ # Cases with 2 ulp error in Python 3.8
841
+ ('0x1.538816d48a13fp+29' , '0x1.7967c5ca43e16p+29' ),
842
+ ('0x1.57b47b7234530p+29' , '0x1.74e2c7040e772p+29' ),
843
+ ('0x1.821b685e9b168p+30' , '0x1.677dc1c1e3dc6p+29' ),
844
+ ('0x1.9e8247f67097bp+29' , '0x1.24bd2dc4f4baep+29' ),
845
+ ('0x1.b73b59e0cb5f9p+29' , '0x1.da899ab784a97p+28' ),
846
+ ('0x1.94a8d2842a7cfp+30' , '0x1.326a51d4d8d8ap+30' ),
847
+ ('0x1.e930b9cd99035p+29' , '0x1.5a1030e18dff9p+30' ),
848
+ ('0x1.1592bbb0e4690p+29' , '0x1.a9c337b33fb9ap+29' ),
849
+ ('0x1.1243a50751fd4p+29' , '0x1.a5a10175622d9p+29' ),
850
+ ('0x1.57a8596e74722p+30' , '0x1.42d1af9d04da9p+30' ),
851
+
852
+ # Cases with 1 ulp error in version fff3c28052e6b0
853
+ ('0x1.ee7dbd9565899p+29' , '0x1.7ab4d6fc6e4b4p+29' ),
854
+ ('0x1.5c6bfbec5c4dcp+30' , '0x1.02511184b4970p+30' ),
855
+ ('0x1.59dcebba995cap+30' , '0x1.50ca7e7c38854p+29' ),
856
+ ('0x1.768cdd94cf5aap+29' , '0x1.9cfdc5571d38ep+29' ),
857
+ ('0x1.dcf137d60262ep+29' , '0x1.1101621990b3ep+30' ),
858
+ ('0x1.3a2d006e288b0p+30' , '0x1.e9a240914326cp+29' ),
859
+ ('0x1.62a32f7f53c61p+29' , '0x1.47eb6cd72684fp+29' ),
860
+ ('0x1.d3bcb60748ef2p+29' , '0x1.3f13c4056312cp+30' ),
861
+ ('0x1.282bdb82f17f3p+30' , '0x1.640ba4c4eed3ap+30' ),
862
+ ('0x1.89d8c423ea0c6p+29' , '0x1.d35dcfe902bc3p+29' ),
863
+ ]:
864
+ x = float .fromhex (hx )
865
+ y = float .fromhex (hy )
866
+ with self .subTest (hx = hx , hy = hy , x = x , y = y ):
867
+ with decimal .localcontext (high_precision ):
868
+ z = float ((Decimal (x )** 2 + Decimal (y )** 2 ).sqrt ())
869
+ self .assertEqual (hypot (x , y ), z )
870
+
805
871
def testDist (self ):
806
872
from decimal import Decimal as D
807
873
from fractions import Fraction as F
@@ -904,8 +970,8 @@ class T(tuple):
904
970
for n in range (32 ):
905
971
p = (fourthmax ,) * n
906
972
q = (0.0 ,) * n
907
- self .assertEqual ( dist (p , q ), fourthmax * math .sqrt (n ))
908
- self .assertEqual ( dist (q , p ), fourthmax * math .sqrt (n ))
973
+ self .assertTrue ( math . isclose ( dist (p , q ), fourthmax * math .sqrt (n ) ))
974
+ self .assertTrue ( math . isclose ( dist (q , p ), fourthmax * math .sqrt (n ) ))
909
975
910
976
# Verify scaling for extremely small values
911
977
for exp in range (32 ):
@@ -968,8 +1034,7 @@ def __index__(self):
968
1034
with self .assertRaises (TypeError ):
969
1035
math .isqrt (value )
970
1036
971
- # Python 3.9
972
- def testlcm (self ):
1037
+ def test_lcm (self ):
973
1038
lcm = math .lcm
974
1039
self .assertEqual (lcm (0 , 0 ), 0 )
975
1040
self .assertEqual (lcm (1 , 0 ), 0 )
@@ -1011,7 +1076,7 @@ def testlcm(self):
1011
1076
self .assertRaises (TypeError , lcm , 120.0 , 84 )
1012
1077
self .assertRaises (TypeError , lcm , 120 , 84.0 )
1013
1078
self .assertRaises (TypeError , lcm , 120 , 0 , 84.0 )
1014
- # self.assertEqual(lcm(MyIndexable(120), MyIndexable(84)), 840) # TODO: RUSTPYTHON
1079
+ self .assertEqual (lcm (MyIndexable (120 ), MyIndexable (84 )), 840 )
1015
1080
1016
1081
def testLdexp (self ):
1017
1082
self .assertRaises (TypeError , math .ldexp )
@@ -1987,7 +2052,7 @@ def test_ulp(self):
1987
2052
1988
2053
# min and max
1989
2054
self .assertEqual (math .ulp (0.0 ),
1990
- sys .float_info .min * sys .float_info .epsilon )
2055
+ sys .float_info .min * sys .float_info .epsilon )
1991
2056
self .assertEqual (math .ulp (FLOAT_MAX ),
1992
2057
FLOAT_MAX - math .nextafter (FLOAT_MAX , - INF ))
1993
2058
@@ -2024,6 +2089,7 @@ def assertIsNaN(self, value):
2024
2089
2025
2090
def assertEqualSign (self , x , y ):
2026
2091
"""Similar to assertEqual(), but compare also the sign with copysign().
2092
+
2027
2093
Function useful to compare signed zeros.
2028
2094
"""
2029
2095
self .assertEqual (x , y )
0 commit comments