mirror of
https://github.com/python/cpython.git
synced 2026-01-06 15:32:22 +00:00
gh-135853: add math.fmax and math.fmin (#135888)
This commit is contained in:
parent
83d04a29a6
commit
2301cdb559
6 changed files with 259 additions and 2 deletions
|
|
@ -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*
|
||||
|
|
@ -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``,
|
||||
|
|
|
|||
|
|
@ -212,7 +212,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`.)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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
Modules/clinic/mathmodule.c.h
generated
108
Modules/clinic/mathmodule.c.h
generated
|
|
@ -84,6 +84,112 @@ PyDoc_STRVAR(math_floor__doc__,
|
|||
#define MATH_FLOOR_METHODDEF \
|
||||
{"floor", (PyCFunction)math_floor, METH_O, math_floor__doc__},
|
||||
|
||||
PyDoc_STRVAR(math_fmax__doc__,
|
||||
"fmax($module, x, y, /)\n"
|
||||
"--\n"
|
||||
"\n"
|
||||
"Return the larger of two floating-point arguments.");
|
||||
|
||||
#define MATH_FMAX_METHODDEF \
|
||||
{"fmax", _PyCFunction_CAST(math_fmax), METH_FASTCALL, math_fmax__doc__},
|
||||
|
||||
static double
|
||||
math_fmax_impl(PyObject *module, double x, double y);
|
||||
|
||||
static PyObject *
|
||||
math_fmax(PyObject *module, PyObject *const *args, Py_ssize_t nargs)
|
||||
{
|
||||
PyObject *return_value = NULL;
|
||||
double x;
|
||||
double y;
|
||||
double _return_value;
|
||||
|
||||
if (!_PyArg_CheckPositional("fmax", nargs, 2, 2)) {
|
||||
goto exit;
|
||||
}
|
||||
if (PyFloat_CheckExact(args[0])) {
|
||||
x = PyFloat_AS_DOUBLE(args[0]);
|
||||
}
|
||||
else
|
||||
{
|
||||
x = PyFloat_AsDouble(args[0]);
|
||||
if (x == -1.0 && PyErr_Occurred()) {
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
if (PyFloat_CheckExact(args[1])) {
|
||||
y = PyFloat_AS_DOUBLE(args[1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
y = PyFloat_AsDouble(args[1]);
|
||||
if (y == -1.0 && PyErr_Occurred()) {
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
_return_value = math_fmax_impl(module, x, y);
|
||||
if ((_return_value == -1.0) && PyErr_Occurred()) {
|
||||
goto exit;
|
||||
}
|
||||
return_value = PyFloat_FromDouble(_return_value);
|
||||
|
||||
exit:
|
||||
return return_value;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(math_fmin__doc__,
|
||||
"fmin($module, x, y, /)\n"
|
||||
"--\n"
|
||||
"\n"
|
||||
"Return the smaller of two floating-point arguments.");
|
||||
|
||||
#define MATH_FMIN_METHODDEF \
|
||||
{"fmin", _PyCFunction_CAST(math_fmin), METH_FASTCALL, math_fmin__doc__},
|
||||
|
||||
static double
|
||||
math_fmin_impl(PyObject *module, double x, double y);
|
||||
|
||||
static PyObject *
|
||||
math_fmin(PyObject *module, PyObject *const *args, Py_ssize_t nargs)
|
||||
{
|
||||
PyObject *return_value = NULL;
|
||||
double x;
|
||||
double y;
|
||||
double _return_value;
|
||||
|
||||
if (!_PyArg_CheckPositional("fmin", nargs, 2, 2)) {
|
||||
goto exit;
|
||||
}
|
||||
if (PyFloat_CheckExact(args[0])) {
|
||||
x = PyFloat_AS_DOUBLE(args[0]);
|
||||
}
|
||||
else
|
||||
{
|
||||
x = PyFloat_AsDouble(args[0]);
|
||||
if (x == -1.0 && PyErr_Occurred()) {
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
if (PyFloat_CheckExact(args[1])) {
|
||||
y = PyFloat_AS_DOUBLE(args[1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
y = PyFloat_AsDouble(args[1]);
|
||||
if (y == -1.0 && PyErr_Occurred()) {
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
_return_value = math_fmin_impl(module, x, y);
|
||||
if ((_return_value == -1.0) && PyErr_Occurred()) {
|
||||
goto exit;
|
||||
}
|
||||
return_value = PyFloat_FromDouble(_return_value);
|
||||
|
||||
exit:
|
||||
return return_value;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(math_signbit__doc__,
|
||||
"signbit($module, x, /)\n"
|
||||
"--\n"
|
||||
|
|
@ -1212,4 +1318,4 @@ math_ulp(PyObject *module, PyObject *arg)
|
|||
exit:
|
||||
return return_value;
|
||||
}
|
||||
/*[clinic end generated code: output=4e3fa94d026f027b input=a9049054013a1b77]*/
|
||||
/*[clinic end generated code: output=4fb180d4c25ff8fa input=a9049054013a1b77]*/
|
||||
|
|
|
|||
|
|
@ -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.",
|
||||
|
|
@ -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},
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue