1-
21# Python test set -- math module
32# XXXX Should not do tests around zero only
43
1918NAN = float ('nan' )
2019INF = float ('inf' )
2120NINF = float ('-inf' )
22-
2321FLOAT_MAX = sys .float_info .max
2422FLOAT_MIN = sys .float_info .min
2523
@@ -43,8 +41,10 @@ def to_ulps(x):
4341 adjacent floats are converted to adjacent integers. Then
4442 abs(ulps(x) - ulps(y)) gives the difference in ulps between two
4543 floats.
44+
4645 The results from this function will only make sense on platforms
4746 where native doubles are represented in IEEE 754 binary64 format.
47+
4848 Note: 0.0 and -0.0 are converted to 0 and -1, respectively.
4949 """
5050 n = struct .unpack ('<q' , struct .pack ('<d' , x ))[0 ]
@@ -81,6 +81,7 @@ def count_set_bits(n):
8181def partial_product (start , stop ):
8282 """Product of integers in range(start, stop, 2), computed recursively.
8383 start and stop should both be odd, with start <= stop.
84+
8485 """
8586 numfactors = (stop - start ) >> 1
8687 if not numfactors :
@@ -94,6 +95,7 @@ def partial_product(start, stop):
9495def py_factorial (n ):
9596 """Factorial of nonnegative integer n, via "Binary Split Factorial Formula"
9697 described at http://www.luschny.de/math/factorial/binarysplitfact.html
98+
9799 """
98100 inner = outer = 1
99101 for i in reversed (range (n .bit_length ())):
@@ -105,6 +107,7 @@ def ulp_abs_check(expected, got, ulp_tol, abs_tol):
105107 """Given finite floats `expected` and `got`, check that they're
106108 approximately equal to within the given number of ulps or the
107109 given absolute tolerance, whichever is bigger.
110+
108111 Returns None on success and an error message on failure.
109112 """
110113 ulp_error = abs (to_ulps (expected ) - to_ulps (got ))
@@ -120,12 +123,14 @@ def ulp_abs_check(expected, got, ulp_tol, abs_tol):
120123
121124def parse_mtestfile (fname ):
122125 """Parse a file with test values
126+
123127 -- starts a comment
124128 blank lines, or lines containing only a comment, are ignored
125129 other lines are expected to have the form
126130 id fn arg -> expected [flag]*
131+
127132 """
128- with open (fname ) as fp :
133+ with open (fname , encoding = "utf-8" ) as fp :
129134 for line in fp :
130135 # strip comments, and skip blank lines
131136 if '--' in line :
@@ -144,10 +149,11 @@ def parse_mtestfile(fname):
144149
145150def parse_testfile (fname ):
146151 """Parse a file with test values
152+
147153 Empty lines or lines starting with -- are ignored
148154 yields id, fn, arg_real, arg_imag, exp_real, exp_imag
149155 """
150- with open (fname ) as fp :
156+ with open (fname , encoding = "utf-8" ) as fp :
151157 for line in fp :
152158 # skip comment lines and blank lines
153159 if line .startswith ('--' ) or not line .strip ():
@@ -170,9 +176,11 @@ def result_check(expected, got, ulp_tol=5, abs_tol=0.0):
170176 """Compare arguments expected and got, as floats, if either
171177 is a float, using a tolerance expressed in multiples of
172178 ulp(expected) or absolutely (if given and greater).
179+
173180 As a convenience, when neither argument is a float, and for
174181 non-finite floats, exact equality is demanded. Also, nan==nan
175182 as far as this function is concerned.
183+
176184 Returns None on success and an error message on failure.
177185 """
178186
@@ -232,6 +240,7 @@ def ftest(self, name, got, expected, ulp_tol=5, abs_tol=0.0):
232240 """Compare arguments expected and got, as floats, if either
233241 is a float, using a tolerance expressed in multiples of
234242 ulp(expected) or absolutely, whichever is greater.
243+
235244 As a convenience, when neither argument is a float, and for
236245 non-finite floats, exact equality is demanded. Also, nan==nan
237246 in this function.
@@ -492,17 +501,11 @@ def testFactorial(self):
492501 self .assertRaises (ValueError , math .factorial , - 1 )
493502 self .assertRaises (ValueError , math .factorial , - 10 ** 100 )
494503
495- # TODO: RUSTPYTHON
496- @unittest .expectedFailure
497504 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 )
506509 self .assertRaises (TypeError , math .factorial , decimal .Decimal ('5' ))
507510 self .assertRaises (TypeError , math .factorial , decimal .Decimal ('5.2' ))
508511 self .assertRaises (TypeError , math .factorial , "5" )
@@ -513,8 +516,7 @@ def testFactorialHugeInputs(self):
513516 # Currently raises OverflowError for inputs that are too large
514517 # to fit into a C long.
515518 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 )
518520
519521 def testFloor (self ):
520522 self .assertRaises (TypeError , math .floor )
@@ -587,7 +589,6 @@ def testfrexp(name, result, expected):
587589 self .assertEqual (math .frexp (NINF )[0 ], NINF )
588590 self .assertTrue (math .isnan (math .frexp (NAN )[0 ]))
589591
590-
591592 @requires_IEEE_754
592593 @unittest .skipIf (HAVE_DOUBLE_ROUNDING ,
593594 "fsum is not exact on machines with double rounding" )
@@ -611,6 +612,7 @@ def msum(iterable):
611612 """Full precision summation. Compute sum(iterable) without any
612613 intermediate accumulation of error. Based on the 'lsum' function
613614 at http://code.activestate.com/recipes/393090/
615+
614616 """
615617 tmant , texp = 0 , 0
616618 for x in iterable :
@@ -684,8 +686,6 @@ def msum(iterable):
684686 s = msum (vals )
685687 self .assertEqual (msum (vals ), math .fsum (vals ))
686688
687-
688- # Python 3.9
689689 def testGcd (self ):
690690 gcd = math .gcd
691691 self .assertEqual (gcd (0 , 0 ), 0 )
@@ -726,7 +726,7 @@ def testGcd(self):
726726 self .assertRaises (TypeError , gcd , 120.0 , 84 )
727727 self .assertRaises (TypeError , gcd , 120 , 84.0 )
728728 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 )
730730
731731 def testHypot (self ):
732732 from decimal import Decimal
@@ -795,13 +795,79 @@ def testHypot(self):
795795 # Verify scaling for extremely large values
796796 fourthmax = FLOAT_MAX / 4.0
797797 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 )))
799800
800801 # Verify scaling for extremely small values
801802 for exp in range (32 ):
802803 scale = FLOAT_MIN / 2.0 ** exp
803804 self .assertEqual (math .hypot (4 * scale , 3 * scale ), 5 * scale )
804805
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+
805871 def testDist (self ):
806872 from decimal import Decimal as D
807873 from fractions import Fraction as F
@@ -904,8 +970,8 @@ class T(tuple):
904970 for n in range (32 ):
905971 p = (fourthmax ,) * n
906972 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 ) ))
909975
910976 # Verify scaling for extremely small values
911977 for exp in range (32 ):
@@ -968,8 +1034,7 @@ def __index__(self):
9681034 with self .assertRaises (TypeError ):
9691035 math .isqrt (value )
9701036
971- # Python 3.9
972- def testlcm (self ):
1037+ def test_lcm (self ):
9731038 lcm = math .lcm
9741039 self .assertEqual (lcm (0 , 0 ), 0 )
9751040 self .assertEqual (lcm (1 , 0 ), 0 )
@@ -1011,7 +1076,7 @@ def testlcm(self):
10111076 self .assertRaises (TypeError , lcm , 120.0 , 84 )
10121077 self .assertRaises (TypeError , lcm , 120 , 84.0 )
10131078 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 )
10151080
10161081 def testLdexp (self ):
10171082 self .assertRaises (TypeError , math .ldexp )
@@ -1987,7 +2052,7 @@ def test_ulp(self):
19872052
19882053 # min and max
19892054 self .assertEqual (math .ulp (0.0 ),
1990- sys .float_info .min * sys .float_info .epsilon )
2055+ sys .float_info .min * sys .float_info .epsilon )
19912056 self .assertEqual (math .ulp (FLOAT_MAX ),
19922057 FLOAT_MAX - math .nextafter (FLOAT_MAX , - INF ))
19932058
@@ -2024,6 +2089,7 @@ def assertIsNaN(self, value):
20242089
20252090 def assertEqualSign (self , x , y ):
20262091 """Similar to assertEqual(), but compare also the sign with copysign().
2092+
20272093 Function useful to compare signed zeros.
20282094 """
20292095 self .assertEqual (x , y )
0 commit comments