Skip to content

gh-135853: add math.fmax and math.fmin #135888

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 12 commits into
base: main
Choose a base branch
from
22 changes: 22 additions & 0 deletions Doc/library/math.rst
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ noted otherwise, all return values are floats.
:func:`fabs(x) <fabs>` Absolute value of *x*
:func:`floor(x) <floor>` Floor of *x*, the largest integer less than or equal to *x*
:func:`fma(x, y, z) <fma>` Fused multiply-add operation: ``(x * y) + z``
:func:`fmax(x, y) <fmax>` Maximum of two floating-point values
:func:`fmin(x, y) <fmin>` Minimum of two floating-point values
:func:`fmod(x, y) <fmod>` Remainder of division ``x / y``
:func:`modf(x) <modf>` Fractional and integer parts of *x*
:func:`remainder(x, y) <remainder>` Remainder of *x* with respect to *y*
Expand Down Expand Up @@ -247,6 +249,26 @@ Floating point arithmetic
.. versionadded:: 3.13


.. function:: fmax(x, y, /)

Get the larger of two floating-point values, treating NaNs as missing data.

When both operands are NaNs, return ``nan`` (the sign of the result is
implementation-defined).

.. versionadded:: next


.. function:: fmin(x, y, /)

Get the smaller of two floating-point values, treating NaNs as missing data.

When both operands are NaNs, return ``nan`` (the sign of the result is
implementation-defined).

.. versionadded:: next


.. function:: fmod(x, y)

Return the floating-point remainder of ``x / y``,
Expand Down
3 changes: 3 additions & 0 deletions Doc/whatsnew/3.15.rst
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,9 @@ math
* Add :func:`math.isnormal` and :func:`math.issubnormal` functions.
(Contributed by Sergey B Kirpichev in :gh:`132908`.)

* Add :func:`math.fmax` and :func:`math.fmin` functions.
(Contributed by Bénédikt Tran in :gh:`135853`.)


os.path
-------
Expand Down
94 changes: 94 additions & 0 deletions Lib/test/test_math.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

eps = 1E-05
NAN = float('nan')
NNAN = float('-nan')
INF = float('inf')
NINF = float('-inf')
FLOAT_MAX = sys.float_info.max
Expand Down Expand Up @@ -623,6 +624,99 @@ def testFmod(self):
self.assertEqual(math.fmod(0.0, NINF), 0.0)
self.assertRaises(ValueError, math.fmod, INF, INF)

def test_fmax(self):
self.assertRaises(TypeError, math.fmax)
self.assertRaises(TypeError, math.fmax, 'x', 'y')

self.assertEqual(math.fmax(0., 0.), 0.)
self.assertEqual(math.fmax(0., -0.), 0.)
self.assertEqual(math.fmax(-0., 0.), 0.)

self.assertEqual(math.fmax(1., 0.), 1.)
self.assertEqual(math.fmax(0., 1.), 1.)
self.assertEqual(math.fmax(1., -0.), 1.)
self.assertEqual(math.fmax(-0., 1.), 1.)

self.assertEqual(math.fmax(-1., 0.), 0.)
self.assertEqual(math.fmax(0., -1.), 0.)
self.assertEqual(math.fmax(-1., -0.), -0.)
self.assertEqual(math.fmax(-0., -1.), -0.)

for x in [NINF, -1., -0., 0., 1., INF]:
self.assertFalse(math.isnan(x))

with self.subTest("math.fmax(INF, x)", x=x):
self.assertEqual(math.fmax(INF, x), INF)
with self.subTest("math.fmax(x, INF)", x=x):
self.assertEqual(math.fmax(x, INF), INF)

with self.subTest("math.fmax(NINF, x)", x=x):
self.assertEqual(math.fmax(NINF, x), x)
with self.subTest("math.fmax(x, NINF)", x=x):
self.assertEqual(math.fmax(x, NINF), x)

@requires_IEEE_754
def test_fmax_nans(self):
# When exactly one operand is NaN, the other is returned.
for x in [NINF, -1., -0., 0., 1., INF]:
with self.subTest(x=x):
self.assertFalse(math.isnan(math.fmax(NAN, x)))
self.assertFalse(math.isnan(math.fmax(x, NAN)))
self.assertFalse(math.isnan(math.fmax(NNAN, x)))
self.assertFalse(math.isnan(math.fmax(x, NNAN)))
# When both operands are NaNs, fmax() returns NaN (see C11, F.10.9.2).
self.assertTrue(math.isnan(math.fmax(NAN, NAN)))
self.assertTrue(math.isnan(math.fmax(NNAN, NNAN)))
self.assertTrue(math.isnan(math.fmax(NAN, NNAN)))
self.assertTrue(math.isnan(math.fmax(NNAN, NAN)))

def test_fmin(self):
self.assertRaises(TypeError, math.fmin)
self.assertRaises(TypeError, math.fmin, 'x', 'y')

self.assertEqual(math.fmin(0., 0.), 0.)
self.assertEqual(math.fmin(0., -0.), -0.)
self.assertEqual(math.fmin(-0., 0.), -0.)

self.assertEqual(math.fmin(1., 0.), 0.)
self.assertEqual(math.fmin(0., 1.), 0.)
self.assertEqual(math.fmin(1., -0.), -0.)
self.assertEqual(math.fmin(-0., 1.), -0.)

self.assertEqual(math.fmin(-1., 0.), -1.)
self.assertEqual(math.fmin(0., -1.), -1.)
self.assertEqual(math.fmin(-1., -0.), -1.)
self.assertEqual(math.fmin(-0., -1.), -1.)

for x in [NINF, -1., -0., 0., 1., INF]:
self.assertFalse(math.isnan(x))

with self.subTest("math.fmin(INF, x)", x=x):
self.assertEqual(math.fmin(INF, x), x)
with self.subTest("math.fmin(x, INF)", x=x):
self.assertEqual(math.fmin(x, INF), x)

with self.subTest("math.fmin(NINF, x)", x=x):
self.assertEqual(math.fmin(NINF, x), NINF)
with self.subTest("math.fmin(x, NINF)", x=x):
self.assertEqual(math.fmin(x, NINF), NINF)

@requires_IEEE_754
def test_fmin_nans(self):
# When exactly one operand is NaN, the other is returned.
for x in [NINF, -1., -0., 0., 1., INF]:
with self.subTest(x=x):
self.assertFalse(math.isnan(x))
self.assertFalse(math.isnan(math.fmin(NAN, x)))
self.assertFalse(math.isnan(math.fmin(x, NAN)))
self.assertFalse(math.isnan(math.fmin(NNAN, x)))
self.assertFalse(math.isnan(math.fmin(x, NNAN)))
# When both operands are NaNs, fmin() returns NaN (see C11, F.10.9.3).
self.assertTrue(math.isnan(math.fmin(NAN, NAN)))
self.assertTrue(math.isnan(math.fmin(NNAN, NNAN)))
self.assertTrue(math.isnan(math.fmin(NAN, NNAN)))
self.assertTrue(math.isnan(math.fmin(NNAN, NAN)))

def testFrexp(self):
self.assertRaises(TypeError, math.frexp)

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Add :func:`math.fmax` and :math:`math.fmin` to get the larger and smaller of
two floating-point values. Patch by Bénédikt Tran.
108 changes: 107 additions & 1 deletion Modules/clinic/mathmodule.c.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

36 changes: 36 additions & 0 deletions Modules/mathmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -1214,6 +1214,40 @@ math_floor(PyObject *module, PyObject *number)
return PyLong_FromDouble(floor(x));
}

/*[clinic input]
math.fmax -> double

x: double
y: double
/

Returns the larger of two floating-point arguments.
[clinic start generated code]*/

static double
math_fmax_impl(PyObject *module, double x, double y)
/*[clinic end generated code: output=00692358d312fee2 input=e64ab9f40a60f4f1]*/
{
return fmax(x, y);
}

/*[clinic input]
math.fmin -> double

x: double
y: double
/

Returns the smaller of two floating-point arguments.
[clinic start generated code]*/

static double
math_fmin_impl(PyObject *module, double x, double y)
/*[clinic end generated code: output=3d5b7826bd292dd9 input=f7b5c91de01d766f]*/
{
return fmin(x, y);
}

FUNC1AD(gamma, m_tgamma,
"gamma($module, x, /)\n--\n\n"
"Gamma function at x.",
Expand Down Expand Up @@ -4175,7 +4209,9 @@ static PyMethodDef math_methods[] = {
MATH_FACTORIAL_METHODDEF
MATH_FLOOR_METHODDEF
MATH_FMA_METHODDEF
MATH_FMAX_METHODDEF
MATH_FMOD_METHODDEF
MATH_FMIN_METHODDEF
MATH_FREXP_METHODDEF
MATH_FSUM_METHODDEF
{"gamma", math_gamma, METH_O, math_gamma_doc},
Expand Down
Loading