8000 gh-135853: add `math.fmax` and `math.fmin` by picnixz · Pull Request #135888 · python/cpython · GitHub
[go: up one dir, main page]

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
26 changes: 26 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 @@ -248,6 +250,30 @@ 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 (signed) NaNs or zeroes, return ``nan`` and ``0``
respectively and the sign of the result is implementation-defined, that
is, :func:`!fmax` is not required to be sensitive to the sign of such
operands (see Annex F of the C11 standard, §F.10.0.3 and §F.10.9.2).

.. versionadded:: next


.. function:: fmin(x, y)

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

When both operands are (signed) NaNs or zeroes, return ``nan`` and ``0``
respectively and the sign of the result is implementation-defined, that
is, :func:`!fmin` is not required to be sensitive to the sign of such
operands (see Annex F of the C11 standard, §F.10.0.3 and §F.10.9.3).

.. versionadded:: next


.. function:: fmod(x, y)

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

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


Expand Down
87 changes: 87 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 @@ -636,6 +637,92 @@ 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(1., 2.), 2.)
self.assertEqual(math.fmax(2., 1.), 2.)

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(x=x, is_negative=math.copysign(1, x) < 0):
self.assertEqual(math.fmax(INF, x), INF)
self.assertEqual(math.fmax(x, INF), INF)
self.assertEqual(math.fmax(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, is_negative=math.copysign(1, x) < 0):
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)
# whose sign is implementation-defined (see C11, F.10.0.3).
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(1., 2.), 1.)
self.assertEqual(math.fmin(2., 1.), 1.)

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(x=x, is_negative=math.copysign(1, x) < 0):
self.assertEqual(math.fmin(INF, x), x)
self.assertEqual(math.fmin(x, INF), x)
self.assertEqual(math.fmin(NINF, x), NINF)
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, is_negative=math.copysign(1, x) < 0):
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)
# whose sign is implementation-defined (see C11, F.10.0.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 :func:`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
/
Return 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=021596c027336ffe]*/
{
return fmax(x, y);
}

/*[clinic input]
math.fmin -> double
x: double
y: double
/
Return 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=d12e64ccc33f878a]*/
{
return fmin(x, y);
}

FUNC1AD(gamma, m_tgamma,
"gamma($module, x, /)\n--\n\n"
"Gamma function at x.",
Expand Down Expand Up @@ -4192,7 +4226,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
0