10000 Update test_math from CPython 3.13.2 · RustPython/RustPython@af42192 · GitHub
[go: up one dir, main page]

Skip to content

Commit af42192

Browse files
committed
Update test_math from CPython 3.13.2
Implemnted fma in math module.
1 parent d7113e1 commit af42192

File tree

5 files changed

+268
-3
lines changed

5 files changed

+268
-3
lines changed

Lib/test/cmath_testcases.txt renamed to Lib/test/mathdata/cmath_testcases.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1536,6 +1536,7 @@ sqrt0141 sqrt -1.797e+308 -9.9999999999999999e+306 -> 3.7284476432057307e+152 -1
15361536
sqrt0150 sqrt 1.7976931348623157e+308 0.0 -> 1.3407807929942596355e+154 0.0
15371537
sqrt0151 sqrt 2.2250738585072014e-308 0.0 -> 1.4916681462400413487e-154 0.0
15381538
sqrt0152 sqrt 5e-324 0.0 -> 2.2227587494850774834e-162 0.0
1539+
sqrt0153 sqrt 5e-324 1.0 -> 0.7071067811865476 0.7071067811865476
15391540

15401541
-- special values
15411542
sqrt1000 sqrt 0.0 0.0 -> 0.0 0.0
@@ -1744,6 +1745,7 @@ cosh0023 cosh 2.218885944363501 2.0015727395883687 -> -1.94294321081968 4.129026
17441745
-- large real part
17451746
cosh0030 cosh 710.5 2.3519999999999999 -> -1.2967465239355998e+308 1.3076707908857333e+308
17461747
cosh0031 cosh -710.5 0.69999999999999996 -> 1.4085466381392499e+308 -1.1864024666450239e+308
1748+
cosh0032 cosh 720.0 0.0 -> inf 0.0 overflow
17471749

17481750
-- Additional real values (mpmath)
17491751
cosh0050 cosh 1e-150 0.0 -> 1.0 0.0
@@ -1853,6 +1855,7 @@ sinh0023 sinh 0.043713693678420068 0.22512549887532657 -> 0.042624198673416713 0
18531855
-- large real part
18541856
sinh0030 sinh 710.5 -2.3999999999999999 -> -1.3579970564885919e+308 -1.24394470907798e+308
18551857
sinh0031 sinh -710.5 0.80000000000000004 -> -1.2830671601735164e+308 1.3210954193997678e+308
1858+
sinh0032 sinh 720.0 0.0 -> inf 0.0 overflow
18561859

18571860
-- Additional real values (mpmath)
18581861
sinh0050 sinh 1e-100 0.0 -> 1.00000000000000002e-100 0.0
File renamed without changes.
File renamed without changes.

Lib/test/test_math.py

+241Lines changed: 241 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,8 @@
3333
else:
3434
file = __file__
3535
test_dir = os.path.dirname(file) or os.curdir
36-
math_testcases = os.path.join(test_dir, 'math_testcases.txt')
37-
test_file = os.path.join(test_dir, 'cmath_testcases.txt')
36+
math_testcases = os.path.join(test_dir, 'mathdata', 'math_testcases.txt')
37+
test_file = os.path.join(test_dir, 'mathdata', 'cmath_testcases.txt')
3838

3939

4040
def to_ulps(x):
@@ -2628,9 +2628,247 @@ def test_fractions(self):
26282628
self.assertAllNotClose(fraction_examples, rel_tol=1e-9)
26292629

26302630

2631+
class FMATests(unittest.TestCase):
2632+
""" Tests for math.fma. """
2633+
2634+
def test_fma_nan_results(self):
2635+
# Selected representative values.
2636+
values = [
2637+
-math.inf, -1e300, -2.3, -1e-300, -0.0,
2638+
0.0, 1e-300, 2.3, 1e300, math.inf, math.nan
2639+
]
2640+
2641+
# If any input is a NaN, the result should be a NaN, too.
2642+
for a, b in itertools.product(values, repeat=2):
2643+
self.assertIsNaN(math.fma(math.nan, a, b))
2644+
self.assertIsNaN(math.fma(a, math.nan, b))
2645+
self.assertIsNaN(math.fma(a, b, math.nan))
2646+
2647+
def test_fma_infinities(self):
2648+
# Cases involving infinite inputs or results.
2649+
positives = [1e-300, 2.3, 1e300, math.inf]
2650+
finites = [-1e300, -2.3, -1e-300, -0.0, 0.0, 1e-300, 2.3, 1e300]
2651+
non_nans = [-math.inf, -2.3, -0.0, 0.0, 2.3, math.inf]
2652+
2653+
# ValueError due to inf * 0 computation.
2654+
for c in non_nans:
2655+
for infinity in [math.inf, -math.inf]:
2656+
for zero in [0.0, -0.0]:
2657+
with self.assertRaises(ValueError):
2658+
math.fma(infinity, zero, c)
2659+
with self.assertRaises(ValueError):
2660+
math.fma(zero, infinity, c)
2661+
2662+
# ValueError when a*b and c both infinite of opposite signs.
2663+
for b in positives:
2664+
with self.assertRaises(ValueError):
2665+
math.fma(math.inf, b, -math.inf)
2666+
with self.assertRaises(ValueError):
2667+
math.fma(math.inf, -b, math.inf)
2668+
with self.assertRaises(ValueError):
2669+
math.fma(-math.inf, -b, -math.inf)
2670+
with self.assertRaises(ValueError):
2671+
math.fma(-math.inf, b, math.inf)
2672+
with self.assertRaises(ValueError):
2673+
math.fma(b, math.inf, -math.inf)
2674+
with self.assertRaises(ValueError):
2675+
math.fma(-b, math.inf, math.inf)
2676+
with self.assertRaises(ValueError):
2677+
math.fma(-b, -math.inf, -math.inf)
2678+
with self.assertRaises(ValueError):
2679+
math.fma(b, -math.inf, math.inf)
2680+
2681+
# Infinite result when a*b and c both infinite of the same sign.
2682+
for b in positives:
2683+
self.assertEqual(math.fma(math.inf, b, math.inf), math.inf)
2684+
self.assertEqual(math.fma(math.inf, -b, -math.inf), -math.inf)
2685+
self.assertEqual(math.fma(-math.inf, -b, math.inf), math.inf)
2686+
self.assertEqual(math.fma(-math.inf, b, -math.inf), -math.inf)
2687+
self.assertEqual(math.fma(b, math.inf, math.inf), math.inf)
2688+
self.assertEqual(math.fma(-b, math.inf, -math.inf), -math.inf)
2689+
self.assertEqual(math.fma(-b, -math.inf, math.inf), math.inf)
2690+
self.assertEqual(math.fma(b, -math.inf, -math.inf), -math.inf)
2691+
2692+
# Infinite result when a*b finite, c infinite.
2693+
for a, b in itertools.product(finites, finites):
2694+
self.assertEqual(math.fma(a, b, math.inf), math.inf)
2695+
self.assertEqual(math.fma(a, b, -math.inf), -math.inf)
2696+
2697+
# Infinite result when a*b infinite, c finite.
2698+
for b, c in itertools.product(positives, finites):
2699+
self.assertEqual(math.fma(math.inf, b, c), math.inf)
2700+
self.assertEqual(math.fma(-math.inf, b, c), -math.inf)
2701+
self.assertEqual(math.fma(-math.inf, -b, c), math.inf)
2702+
self.assertEqual(math.fma(math.inf, -b, c), -math.inf)
2703+
2704+
self.assertEqual(math.fma(b, math.inf, c), math.inf)
2705+
self.assertEqual(math.fma(b, -math.inf, c), -math.inf)
2706+
self.assertEqual(math.fma(-b, -math.inf, c), math.inf)
2707+
self.assertEqual(math.fma(-b, math.inf, c), -math.inf)
2708+
2709+
# gh-73468: On some platforms, libc fma() doesn't implement IEE 754-2008
2710+
# properly: it doesn't use the right sign when the result is zero.
2711+
@unittest.skipIf(
2712+
sys.platform.startswith(("freebsd", "wasi", "netbsd"))
2713+
or (sys.platform == "android" and platform.machine() == "x86_64"),
2714+
f"this platform doesn't implement IEE 754-2008 properly")
2715+
def test_fma_zero_result(self):
2716+
nonnegative_finites = [0.0, 1e-300, 2.3, 1e300]
2717+
2718+
# Zero results from exact zero inputs.
2719+
for b in nonnegative_finites:
2720+
self.assertIsPositiveZero(math.fma(0.0, b, 0.0))
2721+
self.assertIsPositiveZero(math.fma(0.0, b, -0.0))
2722+
self.assertIsNegativeZero(math.fma(0.0, -b, -0.0))
2723+
self.assertIsPositiveZero(math.fma(0.0, -b, 0.0))
2724+
self.assertIsPositiveZero(math.fma(-0.0, -b, 0.0))
2725+
self.assertIsPositiveZero(math.fma(-0.0, -b, -0.0))
2726+
self.assertIsNegativeZero(math.fma(-0.0, b, -0.0))
2727+
self.assertIsPositiveZero(math.fma(-0.0, b, 0.0))
2728+
2729+
self.assertIsPositiveZero(math.fma(b, 0.0, 0.0))
2730+
self.assertIsPositiveZero(math.fma(b, 0.0, -0.0))
2731+
self.assertIsNegativeZero(math.fma(-b, 0.0, -0.0))
2732+
self.assertIsPositiveZero(math.fma(-b, 0.0, 0.0))
2733+
self.assertIsPositiveZero(math.fma(-b, -0.0, 0.0))
2734+
self.assertIsPositiveZero(math.fma(-b, -0.0, -0.0))
2735+
self.assertIsNegativeZero(math.fma(b, -0.0, -0.0))
2736+
self.assertIsPositiveZero(math.fma(b, -0.0, 0.0))
2737+
2738+
# Exact zero result from nonzero inputs.
2739+
self.assertIsPositiveZero(math.fma(2.0, 2.0, -4.0))
2740+
self.assertIsPositiveZero(math.fma(2.0, -2.0, 4.0))
2741+
self.assertIsPositiveZero(math.fma(-2.0, -2.0, -4.0))
2742+
self.assertIsPositiveZero(math.fma(-2.0, 2.0, 4.0))
2743+
2744+
# Underflow to zero.
2745+
tiny = 1e-300
2746+
self.assertIsPositiveZero(math.fma(tiny, tiny, 0.0))
2747+
self.assertIsNegativeZero(math.fma(tiny, -tiny, 0.0))
2748+
self.assertIsPositiveZero(math.fma(-tiny, -tiny, 0.0))
2749+
self.assertIsNegativeZero(math.fma(-tiny, tiny, 0.0))
2750+
self.assertIsPositiveZero(math.fma(tiny, tiny, -0.0))
2751+
self.assertIsNegativeZero(math.fma(tiny, -tiny, -0.0))
2752+
self.assertIsPositiveZero(math.fma(-tiny, -tiny, -0.0))
2753+
self.assertIsNegativeZero(math.fma(-tiny, tiny, -0.0))
2754+
2755+
# Corner case where rounding the multiplication would
2756+
# give the wrong result.
2757+
x = float.fromhex('0x1p-500')
2758+
y = float.fromhex('0x1p-550')
2759+
z = float.fromhex('0x1p-1000')
2760+
self.assertIsNegativeZero(math.fma(x-y, x+y, -z))
2761+
self.assertIsPositiveZero(math.fma(y-x, x+y, z))
2762+
self.assertIsNegativeZero(math.fma(y-x, -(x+y), -z))
2763+
self.assertIsPositiveZero(math.fma(x-y, -(x+y), z))
2764+
2765+
def test_fma_overflow(self):
2766+
a = b = float.fromhex('0x1p512')
2767+
c = float.fromhex('0x1p1023')
2768+
# Overflow from multiplication.
2769+
with self.assertRaises(OverflowError):
2770+
math.fma(a, b, 0.0)
2771+
self.assertEqual(math.fma(a, b/2.0, 0.0), c)
2772+
# Overflow from the addition.
2773+
with self.assertRaises(OverflowError):
2774+
math.fma(a, b/2.0, c)
2775+
# No overflow, even though a*b overflows a float.
2776+
self.assertEqual(math.fma(a, b, -c), c)
2777+
2778+
# Extreme case: a * b is exactly at the overflow boundary, so the
2779+
# tiniest offset makes a difference between overflow and a finite
2780+
# result.
2781+
a = float.fromhex('0x1.ffffffc000000p+511')
2782+
b = float.fromhex('0x1.0000002000000p+512')
2783+
c = float.fromhex('0x0.0000000000001p-1022')
2784+
with self.assertRaises(OverflowError):
2785+
math.fma(a, b, 0.0)
2786+
with self.assertRaises(OverflowError):
2787+
math.fma(a, b, c)
2788+
self.assertEqual(math.fma(a, b, -c),
2789+
float.fromhex('0x1.fffffffffffffp+1023'))
2790+
2791+
# Another extreme case: here a*b is about as large as possible subject
2792+
# to math.fma(a, b, c) being finite.
2793+
a = float.fromhex('0x1.ae565943785f9p+512')
2794+
b = float.fromhex('0x1.3094665de9db8p+512')
2795+
c = float.fromhex('0x1.fffffffffffffp+1023')
2796+
self.assertEqual(math.fma(a, b, -c), c)
2797+
2798+
def test_fma_single_round(self):
2799+
a = float.fromhex('0x1p-50')
2800+
self.assertEqual(math.fma(a - 1.0, a + 1.0, 1.0), a*a)
2801+
2802+
def test_random(self):
2803+
# A collection of randomly generated inputs for which the naive FMA
2804+
# (with two rounds) gives a different result from a singly-rounded FMA.
2805+
2806+
# tuples (a, b, c, expected)
2807+
test_values = [
2808+
('0x1.694adde428b44p-1', '0x1.371b0d64caed7p-1',
2809+
'0x1.f347e7b8deab8p-4', '0x1.19f10da56c8adp-1'),
2810+
('0x1.605401ccc6ad6p-2', '0x1.ce3a40bf56640p-2',
2811+
'0x1.96e3bf7bf2e20p-2', '0x1.1af6d8aa83101p-1'),
2812+
('0x1.e5abd653a67d4p-2', '0x1.a2e400209b3e6p-1',
2813+
'0x1.a90051422ce13p-1', '0x1.37d68cc8c0fbbp+0'),
2814+
('0x1.f94e8efd54700p-2', '0x1.123065c812cebp-1',
2815+
'0x1.458f86fb6ccd0p-1', '0x1.ccdcee26a3ff3p-1'),
2816+
('0x1.bd926f1eedc96p-1', '0x1.eee9ca68c5740p-1',
2817+
'0x1.960c703eb3298p-2', '0x1.3cdcfb4fdb007p+0'),
2818+
('0x1.27348350fbccdp-1', '0x1.3b073914a53f1p-1',
2819+
'0x1.e300da5c2b4cbp-1', '0x1.4c51e9a3c4e29p+0'),
2820+
('0x1.2774f00b3497bp-1', '0x1.7038ec336bff0p-2',
2821+
'0x1.2f6f2ccc3576bp-1', '0x1.99ad9f9c2688bp-1'),
2822+
('0x1.51d5a99300e5cp-1', '0x1.5cd74abd445a1p-1',
2823+
'0x1.8880ab0bbe530p-1', '0x1.3756f96b91129p+0'),
2824+
('0x1.73cb965b821b8p-2', '0x1.218fd3d8d5371p-1',
2825+
'0x1.d1ea966a1f758p-2', '0x1.5217b8fd90119p-1'),
2826+
('0x1.4aa98e890b046p-1', '0x1.954d85dff1041p-1',
2827+
'0x1.122b59317ebdfp-1', '0x1.0bf644b340cc5p+0'),
2828+
('0x1.e28f29e44750fp-1', '0x1.4bcc4fdcd18fep-1',
2829+
'0x1.fd47f81298259p-1', '0x1.9b000afbc9995p+0'),
2830+
('0x1.d2e850717fe78p-3', '0x1.1dd7531c303afp-1',
2831+
'0x1.e0869746a2fc2p-2', '0x1.316df6eb26439p-1'),
2832+
('0x1.cf89c75ee6fbap-2', '0x1.b23decdc66825p-1',
2833+
'0x1.3d1fe76ac6168p-1', '0x1.00d8ea4c12abbp+0'),
2834+
('0x1.3265ae6f05572p-2', '0x1.16d7ec285f7a2p-1',
2835+
'0x1.0b8405b3827fbp-1', '0x1.5ef33c118a001p-1'),
2836+
('0x1.c4d1bf55ec1a5p-1', '0x1.bc59618459e12p-2',
2837+
'0x1.ce5b73dc1773dp-1', '0x1.496cf6164f99bp+0'),
2838+
('0x1.d350026ac3946p-1', '0x1.9a234e149a68cp-2',
2839+
'0x1.f5467b1911fd6p-2', '0x1.b5cee3225caa5p-1'),
2840+
]
2841+
for a_hex, b_hex, c_hex, expected_hex in test_values:
2842+
a = float.fromhex(a_hex)
2843+
b = float.fromhex(b_hex)
2844+
c = float.fromhex(c_hex)
2845+
expected = float.fromhex(expected_hex)
2846+
self.assertEqual(math.fma(a, b, c), expected)
2847+
self.assertEqual(math.fma(b, a, c), expected)
2848+
2849+
# Custom assertions.
2850+
def assertIsNaN(self, value):
2851+
self.assertTrue(
2852+
math.isnan(value),
2853+
msg="Expected a NaN, got {!r}".format(value)
2854+
)
2855+
2856+
def assertIsPositiveZero(self, value):
2857+
self.assertTrue(
2858+
value == 0 and math.copysign(1, value) > 0,
2859+
msg="Expected a positive zero, got {!r}".format(value)
2860+
)
2861+
2862+
def assertIsNegativeZero(self, value):
2863+
self.assertTrue(
2864+
value == 0 and math.copysign(1, value) < 0,
2865+
msg="Expected a negative zero, got {!r}".format(value)
2866+
)
2867+
2868+
26312869
def load_tests(loader, tests, pattern):
26322870
from doctest import DocFileSuite
2633-
tests.addTest(DocFileSuite("ieee754.txt"))
2871+
tests.addTest(DocFileSuite(os.path.join("mathdata", "ieee754.txt")))
26342872
return tests
26352873

26362874
if __name__ == '__main__':

stdlib/src/math.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -975,4 +975,28 @@ mod math {
975975

976976
Ok(result)
977977
}
978+
979+
#[pyfunction]
980+
fn fma(
981+
x: ArgIntoFloat,
982+
y: ArgIntoFloat,
983+
z: ArgIntoFloat,
984+
vm: &VirtualMachine,
985+
) -> PyResult<f64> {
986+
let result = (*x).mul_add(*y, *z);
987+
988+
if result.is_finite() {
989+
return Ok(result);
990+
}
991+
992+
if result.is_nan() {
993+
if !x.is_nan() && !y.is_nan() && !z.is_nan() {
994+
return Err(vm.new_value_error("invalid operation in fma".to_string()));
995+
}
996+
} else if x.is_finite() && y.is_finite() && z.is_finite() {
997+
return Err(vm.new_overflow_error("overflow in fma".to_string()));
998+
}
999+
1000+
Ok(result)
1001+
}
9781002
}

0 commit comments

Comments
 (0)
0