Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit6e39fa1

Browse files
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>
1 parentc3f43bf commit6e39fa1

File tree

10 files changed

+223
-18
lines changed

10 files changed

+223
-18
lines changed

‎Doc/library/math.rst‎

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -224,11 +224,11 @@ Number-theoretic and representation functions
224224
of *x* and are floats.
225225

226226

227-
..function::nextafter(x, y)
227+
..function::nextafter(x, y, steps=1)
228228

229-
Return thenextfloating-point value after *x* towards *y*.
229+
Return the floating-point value *steps* steps after *x* towards *y*.
230230

231-
If *x* is equal to *y*, return *y*.
231+
If *x* is equal to *y*, return *y*, unless *steps* is zero.
232232

233233
Examples:
234234

@@ -239,6 +239,9 @@ Number-theoretic and representation functions
239239

240240
See also:func:`math.ulp`.
241241

242+
..versionchanged::3.12
243+
Added the *steps* argument.
244+
242245
..versionadded::3.9
243246

244247
..function::perm(n, k=None)

‎Include/internal/pycore_global_objects_fini_generated.h‎

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more aboutcustomizing how changed files appear on GitHub.

‎Include/internal/pycore_global_strings.h‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -681,6 +681,7 @@ struct _Py_global_strings {
681681
STRUCT_FOR_ID(stdin)
682682
STRUCT_FOR_ID(stdout)
683683
STRUCT_FOR_ID(step)
684+
STRUCT_FOR_ID(steps)
684685
STRUCT_FOR_ID(store_name)
685686
STRUCT_FOR_ID(strategy)
686687
STRUCT_FOR_ID(strftime)

‎Include/internal/pycore_runtime_init_generated.h‎

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more aboutcustomizing how changed files appear on GitHub.

‎Include/internal/pycore_unicodeobject_generated.h‎

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more aboutcustomizing how changed files appear on GitHub.

‎Lib/test/test_math.py‎

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2296,11 +2296,20 @@ def test_nextafter(self):
22962296
float.fromhex('0x1.fffffffffffffp-1'))
22972297
self.assertEqual(math.nextafter(1.0,INF),
22982298
float.fromhex('0x1.0000000000001p+0'))
2299+
self.assertEqual(math.nextafter(1.0,-INF,steps=1),
2300+
float.fromhex('0x1.fffffffffffffp-1'))
2301+
self.assertEqual(math.nextafter(1.0,INF,steps=1),
2302+
float.fromhex('0x1.0000000000001p+0'))
2303+
self.assertEqual(math.nextafter(1.0,-INF,steps=3),
2304+
float.fromhex('0x1.ffffffffffffdp-1'))
2305+
self.assertEqual(math.nextafter(1.0,INF,steps=3),
2306+
float.fromhex('0x1.0000000000003p+0'))
22992307

23002308
# x == y: y is returned
2301-
self.assertEqual(math.nextafter(2.0,2.0),2.0)
2302-
self.assertEqualSign(math.nextafter(-0.0,+0.0),+0.0)
2303-
self.assertEqualSign(math.nextafter(+0.0,-0.0),-0.0)
2309+
forstepsinrange(1,5):
2310+
self.assertEqual(math.nextafter(2.0,2.0,steps=steps),2.0)
2311+
self.assertEqualSign(math.nextafter(-0.0,+0.0,steps=steps),+0.0)
2312+
self.assertEqualSign(math.nextafter(+0.0,-0.0,steps=steps),-0.0)
23042313

23052314
# around 0.0
23062315
smallest_subnormal=sys.float_info.min*sys.float_info.epsilon
@@ -2325,6 +2334,11 @@ def test_nextafter(self):
23252334
self.assertIsNaN(math.nextafter(1.0,NAN))
23262335
self.assertIsNaN(math.nextafter(NAN,NAN))
23272336

2337+
self.assertEqual(1.0,math.nextafter(1.0,INF,steps=0))
2338+
withself.assertRaises(ValueError):
2339+
math.nextafter(1.0,INF,steps=-1)
2340+
2341+
23282342
@requires_IEEE_754
23292343
deftest_ulp(self):
23302344
self.assertEqual(math.ulp(1.0),sys.float_info.epsilon)

‎Lib/test/test_math_property.py‎

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
importfunctools
2+
importunittest
3+
frommathimportisnan,nextafter
4+
fromtest.supportimportrequires_IEEE_754
5+
fromtest.support.hypothesis_helperimporthypothesis
6+
7+
floats=hypothesis.strategies.floats
8+
integers=hypothesis.strategies.integers
9+
10+
11+
defassert_equal_float(x,y):
12+
assertisnan(x)andisnan(y)orx==y
13+
14+
15+
defvia_reduce(x,y,steps):
16+
returnfunctools.reduce(nextafter, [y]*steps,x)
17+
18+
19+
classNextafterTests(unittest.TestCase):
20+
@requires_IEEE_754
21+
@hypothesis.given(
22+
x=floats(),
23+
y=floats(),
24+
steps=integers(min_value=0,max_value=2**16))
25+
deftest_count(self,x,y,steps):
26+
assert_equal_float(via_reduce(x,y,steps),
27+
nextafter(x,y,steps=steps))
28+
29+
@requires_IEEE_754
30+
@hypothesis.given(
31+
x=floats(),
32+
y=floats(),
33+
a=integers(min_value=0),
34+
b=integers(min_value=0))
35+
deftest_addition_commutes(self,x,y,a,b):
36+
first=nextafter(x,y,steps=a)
37+
second=nextafter(first,y,steps=b)
38+
combined=nextafter(x,y,steps=a+b)
39+
hypothesis.note(f"{first} ->{second} =={combined}")
40+
41+
assert_equal_float(second,combined)
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Support multiple steps in:func:`math.nextafter`. Patch by Shantanu Jain and Matthias Gorgens.

‎Modules/clinic/mathmodule.c.h‎

Lines changed: 47 additions & 8 deletions
Some generated files are not rendered by default. Learn more aboutcustomizing how changed files appear on GitHub.

‎Modules/mathmodule.c‎

Lines changed: 105 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3864,13 +3864,20 @@ math.nextafter
38643864
x: double
38653865
y: double
38663866
/
3867+
*
3868+
steps: object = None
3869+
3870+
Return the floating-point value the given number of steps after x towards y.
3871+
3872+
If steps is not specified or is None, it defaults to 1.
38673873
3868-
Return the next floating-point value after x towards y.
3874+
Raises a TypeError, if x or y is not a double, or if steps is not an integer.
3875+
Raises ValueError if steps is negative.
38693876
[clinic start generated code]*/
38703877

38713878
staticPyObject*
3872-
math_nextafter_impl(PyObject*module,doublex,doubley)
3873-
/*[clinic end generated code: output=750c8266c1c540ce input=02b2d50cd1d9f9b6]*/
3879+
math_nextafter_impl(PyObject*module,doublex,doubley,PyObject*steps)
3880+
/*[clinic end generated code: output=cc6511f02afc099e input=7f2a5842112af2b4]*/
38743881
{
38753882
#if defined(_AIX)
38763883
if (x==y) {
@@ -3885,7 +3892,101 @@ math_nextafter_impl(PyObject *module, double x, double y)
38853892
returnPyFloat_FromDouble(y);
38863893
}
38873894
#endif
3888-
returnPyFloat_FromDouble(nextafter(x,y));
3895+
if (steps==Py_None) {
3896+
// fast path: we default to one step.
3897+
returnPyFloat_FromDouble(nextafter(x,y));
3898+
}
3899+
steps=PyNumber_Index(steps);
3900+
if (steps==NULL) {
3901+
returnNULL;
3902+
}
3903+
assert(PyLong_CheckExact(steps));
3904+
if (_PyLong_IsNegative((PyLongObject*)steps)) {
3905+
PyErr_SetString(PyExc_ValueError,
3906+
"steps must be a non-negative integer");
3907+
Py_DECREF(steps);
3908+
returnNULL;
3909+
}
3910+
3911+
unsigned long longusteps_ull=PyLong_AsUnsignedLongLong(steps);
3912+
// Conveniently, uint64_t and double have the same number of bits
3913+
// on all the platforms we care about.
3914+
// So if an overflow occurs, we can just use UINT64_MAX.
3915+
Py_DECREF(steps);
3916+
if (usteps_ull >=UINT64_MAX) {
3917+
// This branch includes the case where an error occurred, since
3918+
// (unsigned long long)(-1) = ULLONG_MAX >= UINT64_MAX. Note that
3919+
// usteps_ull can be strictly larger than UINT64_MAX on a machine
3920+
// where unsigned long long has width > 64 bits.
3921+
if (PyErr_Occurred()) {
3922+
if (PyErr_ExceptionMatches(PyExc_OverflowError)) {
3923+
PyErr_Clear();
3924+
}
3925+
else {
3926+
returnNULL;
3927+
}
3928+
}
3929+
usteps_ull=UINT64_MAX;
3930+
}
3931+
assert(usteps_ull <=UINT64_MAX);
3932+
uint64_tusteps= (uint64_t)usteps_ull;
3933+
3934+
if (usteps==0) {
3935+
returnPyFloat_FromDouble(x);
3936+
}
3937+
if (Py_IS_NAN(x)) {
3938+
returnPyFloat_FromDouble(x);
3939+
}
3940+
if (Py_IS_NAN(y)) {
3941+
returnPyFloat_FromDouble(y);
3942+
}
3943+
3944+
// We assume that double and uint64_t have the same endianness.
3945+
// This is not guaranteed by the C-standard, but it is true for
3946+
// all platforms we care about. (The most likely form of violation
3947+
// would be a "mixed-endian" double.)
3948+
unionpun {doublef;uint64_ti;};
3949+
unionpunux= {x},uy= {y};
3950+
if (ux.i==uy.i) {
3951+
returnPyFloat_FromDouble(x);
3952+
}
3953+
3954+
constuint64_tsign_bit=1ULL<<63;
3955+
3956+
uint64_tax=ux.i& ~sign_bit;
3957+
uint64_tay=uy.i& ~sign_bit;
3958+
3959+
// opposite signs
3960+
if (((ux.i ^uy.i)&sign_bit)) {
3961+
// NOTE: ax + ay can never overflow, because their most significant bit
3962+
// ain't set.
3963+
if (ax+ay <=usteps) {
3964+
returnPyFloat_FromDouble(uy.f);
3965+
// This comparison has to use <, because <= would get +0.0 vs -0.0
3966+
// wrong.
3967+
}elseif (ax<usteps) {
3968+
unionpunresult= {.i= (uy.i&sign_bit) | (usteps-ax)};
3969+
returnPyFloat_FromDouble(result.f);
3970+
}else {
3971+
ux.i-=usteps;
3972+
returnPyFloat_FromDouble(ux.f);
3973+
}
3974+
// same sign
3975+
}elseif (ax>ay) {
3976+
if (ax-ay >=usteps) {
3977+
ux.i-=usteps;
3978+
returnPyFloat_FromDouble(ux.f);
3979+
}else {
3980+
returnPyFloat_FromDouble(uy.f);
3981+
}
3982+
}else {
3983+
if (ay-ax >=usteps) {
3984+
ux.i+=usteps;
3985+
returnPyFloat_FromDouble(ux.f);
3986+
}else {
3987+
returnPyFloat_FromDouble(uy.f);
3988+
}
3989+
}
38893990
}
38903991

38913992

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp