Skip to content

Commit 2301cdb

Browse files
authored
gh-135853: add math.fmax and math.fmin (#135888)
1 parent 83d04a2 commit 2301cdb

File tree

6 files changed

+259
-2
lines changed

6 files changed

+259
-2
lines changed

Doc/library/math.rst

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ noted otherwise, all return values are floats.
4242
:func:`fabs(x) <fabs>` Absolute value of *x*
4343
:func:`floor(x) <floor>` Floor of *x*, the largest integer less than or equal to *x*
4444
:func:`fma(x, y, z) <fma>` Fused multiply-add operation: ``(x * y) + z``
45+
:func:`fmax(x, y) <fmax>` Maximum of two floating-point values
46+
:func:`fmin(x, y) <fmin>` Minimum of two floating-point values
4547
:func:`fmod(x, y) <fmod>` Remainder of division ``x / y``
4648
:func:`modf(x) <modf>` Fractional and integer parts of *x*
4749
:func:`remainder(x, y) <remainder>` Remainder of *x* with respect to *y*
@@ -248,6 +250,30 @@ Floating point arithmetic
248250
.. versionadded:: 3.13
249251

250252

253+
.. function:: fmax(x, y)
254+
255+
Get the larger of two floating-point values, treating NaNs as missing data.
256+
257+
When both operands are (signed) NaNs or zeroes, return ``nan`` and ``0``
258+
respectively and the sign of the result is implementation-defined, that
259+
is, :func:`!fmax` is not required to be sensitive to the sign of such
260+
operands (see Annex F of the C11 standard, §F.10.0.3 and §F.10.9.2).
261+
262+
.. versionadded:: next
263+
264+
265+
.. function:: fmin(x, y)
266+
267+
Get the smaller of two floating-point values, treating NaNs as missing data.
268+
269+
When both operands are (signed) NaNs or zeroes, return ``nan`` and ``0``
270+
respectively and the sign of the result is implementation-defined, that
271+
is, :func:`!fmin` is not required to be sensitive to the sign of such
272+
operands (see Annex F of the C11 standard, §F.10.0.3 and §F.10.9.3).
273+
274+
.. versionadded:: next
275+
276+
251277
.. function:: fmod(x, y)
252278

253279
Return the floating-point remainder of ``x / y``,

Doc/whatsnew/3.15.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,7 @@ math
212212
* Add :func:`math.isnormal` and :func:`math.issubnormal` functions.
213213
(Contributed by Sergey B Kirpichev in :gh:`132908`.)
214214

215-
* Add :func:`math.signbit` function.
215+
* Add :func:`math.fmax`, :func:`math.fmin` and :func:`math.signbit` functions.
216216
(Contributed by Bénédikt Tran in :gh:`135853`.)
217217

218218

Lib/test/test_math.py

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
eps = 1E-05
1919
NAN = float('nan')
20+
NNAN = float('-nan')
2021
INF = float('inf')
2122
NINF = float('-inf')
2223
FLOAT_MAX = sys.float_info.max
@@ -636,6 +637,92 @@ def testFmod(self):
636637
self.assertEqual(math.fmod(0.0, NINF), 0.0)
637638
self.assertRaises(ValueError, math.fmod, INF, INF)
638639

640+
def test_fmax(self):
641+
self.assertRaises(TypeError, math.fmax)
642+
self.assertRaises(TypeError, math.fmax, 'x', 'y')
643+
644+
self.assertEqual(math.fmax(0., 0.), 0.)
645+
self.assertEqual(math.fmax(1., 2.), 2.)
646+
self.assertEqual(math.fmax(2., 1.), 2.)
647+
648+
self.assertEqual(math.fmax(+1., +0.), 1.)
649+
self.assertEqual(math.fmax(+0., +1.), 1.)
650+
self.assertEqual(math.fmax(+1., -0.), 1.)
651+
self.assertEqual(math.fmax(-0., +1.), 1.)
652+
653+
self.assertEqual(math.fmax(-1., +0.), 0.)
654+
self.assertEqual(math.fmax(+0., -1.), 0.)
655+
self.assertEqual(math.fmax(-1., -0.), 0.)
656+
self.assertEqual(math.fmax(-0., -1.), 0.)
657+
658+
for x in [NINF, -1., -0., 0., 1., INF]:
659+
self.assertFalse(math.isnan(x))
660+
661+
with self.subTest(x=x, is_negative=math.copysign(1, x) < 0):
662+
self.assertEqual(math.fmax(INF, x), INF)
663+
self.assertEqual(math.fmax(x, INF), INF)
664+
self.assertEqual(math.fmax(NINF, x), x)
665+
self.assertEqual(math.fmax(x, NINF), x)
666+
667+
@requires_IEEE_754
668+
def test_fmax_nans(self):
669+
# When exactly one operand is NaN, the other is returned.
670+
for x in [NINF, -1., -0., 0., 1., INF]:
671+
with self.subTest(x=x, is_negative=math.copysign(1, x) < 0):
672+
self.assertFalse(math.isnan(math.fmax(NAN, x)))
673+
self.assertFalse(math.isnan(math.fmax(x, NAN)))
674+
self.assertFalse(math.isnan(math.fmax(NNAN, x)))
675+
self.assertFalse(math.isnan(math.fmax(x, NNAN)))
676+
# When both operands are NaNs, fmax() returns NaN (see C11, F.10.9.2)
677+
# whose sign is implementation-defined (see C11, F.10.0.3).
678+
self.assertTrue(math.isnan(math.fmax(NAN, NAN)))
679+
self.assertTrue(math.isnan(math.fmax(NNAN, NNAN)))
680+
self.assertTrue(math.isnan(math.fmax(NAN, NNAN)))
681+
self.assertTrue(math.isnan(math.fmax(NNAN, NAN)))
682+
683+
def test_fmin(self):
684+
self.assertRaises(TypeError, math.fmin)
685+
self.assertRaises(TypeError, math.fmin, 'x', 'y')
686+
687+
self.assertEqual(math.fmin(0., 0.), 0.)
688+
self.assertEqual(math.fmin(1., 2.), 1.)
689+
self.assertEqual(math.fmin(2., 1.), 1.)
690+
691+
self.assertEqual(math.fmin(+1., +0.), 0.)
692+
self.assertEqual(math.fmin(+0., +1.), 0.)
693+
self.assertEqual(math.fmin(+1., -0.), 0.)
694+
self.assertEqual(math.fmin(-0., +1.), 0.)
695+
696+
self.assertEqual(math.fmin(-1., +0.), -1.)
697+
self.assertEqual(math.fmin(+0., -1.), -1.)
698+
self.assertEqual(math.fmin(-1., -0.), -1.)
699+
self.assertEqual(math.fmin(-0., -1.), -1.)
700+
701+
for x in [NINF, -1., -0., 0., 1., INF]:
702+
self.assertFalse(math.isnan(x))
703+
704+
with self.subTest(x=x, is_negative=math.copysign(1, x) < 0):
705+
self.assertEqual(math.fmin(INF, x), x)
706+
self.assertEqual(math.fmin(x, INF), x)
707+
self.assertEqual(math.fmin(NINF, x), NINF)
708+
self.assertEqual(math.fmin(x, NINF), NINF)
709+
710+
@requires_IEEE_754
711+
def test_fmin_nans(self):
712+
# When exactly one operand is NaN, the other is returned.
713+
for x in [NINF, -1., -0., 0., 1., INF]:
714+
with self.subTest(x=x, is_negative=math.copysign(1, x) < 0):
715+
self.assertFalse(math.isnan(math.fmin(NAN, x)))
716+
self.assertFalse(math.isnan(math.fmin(x, NAN)))
717+
self.assertFalse(math.isnan(math.fmin(NNAN, x)))
718+
self.assertFalse(math.isnan(math.fmin(x, NNAN)))
719+
# When both operands are NaNs, fmin() returns NaN (see C11, F.10.9.3)
720+
# whose sign is implementation-defined (see C11, F.10.0.3).
721+
self.assertTrue(math.isnan(math.fmin(NAN, NAN)))
722+
self.assertTrue(math.isnan(math.fmin(NNAN, NNAN)))
723+
self.assertTrue(math.isnan(math.fmin(NAN, NNAN)))
724+
self.assertTrue(math.isnan(math.fmin(NNAN, NAN)))
725+
639726
def testFrexp(self):
640727
self.assertRaises(TypeError, math.frexp)
641728

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Add :func:`math.fmax` and :func:`math.fmin` to get the larger and smaller of
2+
two floating-point values. Patch by Bénédikt Tran.

Modules/clinic/mathmodule.c.h

Lines changed: 107 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Modules/mathmodule.c

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1214,6 +1214,40 @@ math_floor(PyObject *module, PyObject *number)
12141214
return PyLong_FromDouble(floor(x));
12151215
}
12161216

1217+
/*[clinic input]
1218+
math.fmax -> double
1219+
1220+
x: double
1221+
y: double
1222+
/
1223+
1224+
Return the larger of two floating-point arguments.
1225+
[clinic start generated code]*/
1226+
1227+
static double
1228+
math_fmax_impl(PyObject *module, double x, double y)
1229+
/*[clinic end generated code: output=00692358d312fee2 input=021596c027336ffe]*/
1230+
{
1231+
return fmax(x, y);
1232+
}
1233+
1234+
/*[clinic input]
1235+
math.fmin -> double
1236+
1237+
x: double
1238+
y: double
1239+
/
1240+
1241+
Return the smaller of two floating-point arguments.
1242+
[clinic start generated code]*/
1243+
1244+
static double
1245+
math_fmin_impl(PyObject *module, double x, double y)
1246+
/*[clinic end generated code: output=3d5b7826bd292dd9 input=d12e64ccc33f878a]*/
1247+
{
1248+
return fmin(x, y);
1249+
}
1250+
12171251
FUNC1AD(gamma, m_tgamma,
12181252
"gamma($module, x, /)\n--\n\n"
12191253
"Gamma function at x.",
@@ -4192,7 +4226,9 @@ static PyMethodDef math_methods[] = {
41924226
MATH_FACTORIAL_METHODDEF
41934227
MATH_FLOOR_METHODDEF
41944228
MATH_FMA_METHODDEF
4229+
MATH_FMAX_METHODDEF
41954230
MATH_FMOD_METHODDEF
4231+
MATH_FMIN_METHODDEF
41964232
MATH_FREXP_METHODDEF
41974233
MATH_FSUM_METHODDEF
41984234
{"gamma", math_gamma, METH_O, math_gamma_doc},

0 commit comments

Comments
 (0)