gh-94906: Support multiple steps in math.nextafter (#103881)

This PR updates `math.nextafter` to add a new `steps` argument. The behaviour is as though `math.nextafter` had been called `steps` times in succession.

---------

Co-authored-by: Mark Dickinson <mdickinson@enthought.com>
This commit is contained in:
Matthias Görgens 2023-05-20 04:03:49 +08:00 committed by GitHub
parent c3f43bfb4b
commit 6e39fa1955
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 223 additions and 18 deletions

View file

@ -2296,11 +2296,20 @@ class MathTests(unittest.TestCase):
float.fromhex('0x1.fffffffffffffp-1'))
self.assertEqual(math.nextafter(1.0, INF),
float.fromhex('0x1.0000000000001p+0'))
self.assertEqual(math.nextafter(1.0, -INF, steps=1),
float.fromhex('0x1.fffffffffffffp-1'))
self.assertEqual(math.nextafter(1.0, INF, steps=1),
float.fromhex('0x1.0000000000001p+0'))
self.assertEqual(math.nextafter(1.0, -INF, steps=3),
float.fromhex('0x1.ffffffffffffdp-1'))
self.assertEqual(math.nextafter(1.0, INF, steps=3),
float.fromhex('0x1.0000000000003p+0'))
# x == y: y is returned
self.assertEqual(math.nextafter(2.0, 2.0), 2.0)
self.assertEqualSign(math.nextafter(-0.0, +0.0), +0.0)
self.assertEqualSign(math.nextafter(+0.0, -0.0), -0.0)
for steps in range(1, 5):
self.assertEqual(math.nextafter(2.0, 2.0, steps=steps), 2.0)
self.assertEqualSign(math.nextafter(-0.0, +0.0, steps=steps), +0.0)
self.assertEqualSign(math.nextafter(+0.0, -0.0, steps=steps), -0.0)
# around 0.0
smallest_subnormal = sys.float_info.min * sys.float_info.epsilon
@ -2325,6 +2334,11 @@ class MathTests(unittest.TestCase):
self.assertIsNaN(math.nextafter(1.0, NAN))
self.assertIsNaN(math.nextafter(NAN, NAN))
self.assertEqual(1.0, math.nextafter(1.0, INF, steps=0))
with self.assertRaises(ValueError):
math.nextafter(1.0, INF, steps=-1)
@requires_IEEE_754
def test_ulp(self):
self.assertEqual(math.ulp(1.0), sys.float_info.epsilon)

View file

@ -0,0 +1,41 @@
import functools
import unittest
from math import isnan, nextafter
from test.support import requires_IEEE_754
from test.support.hypothesis_helper import hypothesis
floats = hypothesis.strategies.floats
integers = hypothesis.strategies.integers
def assert_equal_float(x, y):
assert isnan(x) and isnan(y) or x == y
def via_reduce(x, y, steps):
return functools.reduce(nextafter, [y] * steps, x)
class NextafterTests(unittest.TestCase):
@requires_IEEE_754
@hypothesis.given(
x=floats(),
y=floats(),
steps=integers(min_value=0, max_value=2**16))
def test_count(self, x, y, steps):
assert_equal_float(via_reduce(x, y, steps),
nextafter(x, y, steps=steps))
@requires_IEEE_754
@hypothesis.given(
x=floats(),
y=floats(),
a=integers(min_value=0),
b=integers(min_value=0))
def test_addition_commutes(self, x, y, a, b):
first = nextafter(x, y, steps=a)
second = nextafter(first, y, steps=b)
combined = nextafter(x, y, steps=a+b)
hypothesis.note(f"{first} -> {second} == {combined}")
assert_equal_float(second, combined)