Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions doc/source/reference/routines.err.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,50 @@
.. _routines.err:

Floating point error handling
*****************************

NumPy supports floating-point exceptions as defined in the IEEE 745
standard [1]_:

- Invalid operation: result is not an expressible number. This typically
indicates that a new NaN (not a number) was produced, e.g. by ``0. / 0.``.
- Division by zero: infinite result obtained from finite numbers,
e.g. by ``1. / 0.``
- Overflow: result too large to be expressed.
- Underflow: result so close to zero that some precision was lost.

NumPy does not warn for comparisons involving a NaN, such as ``NaN < 0.``.
This differs from the IEEE standard, which specifies warning for the default
operators: ``<, <=, >, >=`` when at least one value is a NaN.

The floating-point error handling can be customized using the below functions.
By default all except "underflow" give a warning.

In some cases NumPy will use these floating point error settings
also for Integer operations.

.. admonition:: Advanced details

* Floating-point errors rely on the compiler, hardware, and math library.
NumPy tries to ensure correct warning behavior, but some systems may
have incomplete support or choose speed over correct floating-point errors.
For example the MacOS math library is known to not indicate some errors.

* IEEE defines a "signalling NaN" or sNaN. NumPy will never create these.
If you import data containing such an sNaN you may see unexpected
warnings. In that case you can use::

with np.errstate(invalid="ignore"):
np.add(arr, 0, out=arr)

to convert all signalling NaNs to normal (quiet) ones. Signalling NaNs
are expected to signal an error on almost all operations. However, NumPy
may not always behave IEEE conform with respect to warnings.


.. [1] https://en.wikipedia.org/wiki/IEEE_754


.. currentmodule:: numpy

Setting and getting error handling
Expand Down
13 changes: 7 additions & 6 deletions numpy/core/_ufunc_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,14 +71,15 @@ def seterr(all=None, divide=None, over=None, under=None, invalid=None):

Notes
-----
The floating-point exceptions are defined in the IEEE 754 standard [1]_:
See also :ref:`routines.err`. The floating-point exceptions follow
those defined in the IEEE 745 standard [1]_:

- Division by zero: infinite result obtained from finite numbers.
- Invalid operation: result is not an expressible number. This typically
indicates that a new NaN (not a number) was produced, e.g. by ``0. / 0.``
- Division by zero: infinite result obtained from finite numbers,
e.g. by ``1. / 0.``
- Overflow: result too large to be expressed.
- Underflow: result so close to zero that some precision
was lost.
- Invalid operation: result is not an expressible number, typically
indicates that a NaN was produced.
- Underflow: result so close to zero that some precision was lost.

.. [1] https://en.wikipedia.org/wiki/IEEE_754

Expand Down
80 changes: 48 additions & 32 deletions numpy/core/src/umath/loops.c.src
Original file line number Diff line number Diff line change
Expand Up @@ -1595,9 +1595,8 @@ NPY_NO_EXPORT NPY_GCC_OPT_3 void
* #C = F, , L#
*/
/**begin repeat1
* #kind = equal, not_equal, less, less_equal, greater, greater_equal,
* logical_and, logical_or#
* #OP = ==, !=, <, <=, >, >=, &&, ||#
* #kind = equal, not_equal, logical_and, logical_or#
* #OP = ==, !=, &&, ||#
*/
NPY_NO_EXPORT void
@TYPE@_@kind@(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func))
Expand All @@ -1609,7 +1608,29 @@ NPY_NO_EXPORT void
*((npy_bool *)op1) = in1 @OP@ in2;
}
}
npy_clear_floatstatus_barrier((char*)dimensions);
}
/**end repeat1**/

/**begin repeat1
* #kind = less, less_equal, greater, greater_equal#
* #comp = isless, islessequal, isgreater, isgreaterequal#
*/
/*
* Note: Unlike the operators, the functions `isless`, etc. do not set
* floating point exceptions. NumPy currently does not warn for
* comparisons like `NaN > 0`, using the operators would add the
* warning, as it is the main/default comparison as per IEEE.
*/
NPY_NO_EXPORT void
@TYPE@_@kind@(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func))
{
if (!run_binary_simd_@kind@_@TYPE@(args, dimensions, steps)) {
BINARY_LOOP {
const @type@ in1 = *(@type@ *)ip1;
const @type@ in2 = *(@type@ *)ip2;
*((npy_bool *)op1) = @comp@(in1, in2);
}
}
}
/**end repeat1**/

Expand Down Expand Up @@ -1650,7 +1671,6 @@ NPY_NO_EXPORT void
*((npy_bool *)op1) = @func@(in1) != 0;
}
}
npy_clear_floatstatus_barrier((char*)dimensions);
}
/**end repeat2**/
/**end repeat1**/
Expand Down Expand Up @@ -1686,7 +1706,7 @@ NPY_NO_EXPORT void

/**begin repeat1
* #kind = maximum, minimum#
* #OP = >=, <=#
* #comp = isgreaterequal, islessequal#
**/
NPY_NO_EXPORT void
@TYPE@_@kind@_avx512f(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func))
Expand All @@ -1697,7 +1717,7 @@ NPY_NO_EXPORT void
BINARY_REDUCE_LOOP(@type@) {
const @type@ in2 = *(@type@ *)ip2;
/* Order of operations important for MSVC 2015 */
io1 = (io1 @OP@ in2 || npy_isnan(io1)) ? io1 : in2;
io1 = (@comp@(io1, in2) || npy_isnan(io1)) ? io1 : in2;
}
*((@type@ *)iop1) = io1;
}
Expand All @@ -1708,12 +1728,11 @@ NPY_NO_EXPORT void
@type@ in1 = *(@type@ *)ip1;
const @type@ in2 = *(@type@ *)ip2;
/* Order of operations important for MSVC 2015 */
in1 = (in1 @OP@ in2 || npy_isnan(in1)) ? in1 : in2;
in1 = (@comp@(in1, in2) || npy_isnan(in1)) ? in1 : in2;
*((@type@ *)op1) = in1;
}
}
}
npy_clear_floatstatus_barrier((char*)dimensions);
}

NPY_NO_EXPORT void
Expand All @@ -1725,7 +1744,7 @@ NPY_NO_EXPORT void
BINARY_REDUCE_LOOP(@type@) {
const @type@ in2 = *(@type@ *)ip2;
/* Order of operations important for MSVC 2015 */
io1 = (io1 @OP@ in2 || npy_isnan(io1)) ? io1 : in2;
io1 = (@comp@(io1, in2) || npy_isnan(io1)) ? io1 : in2;
}
*((@type@ *)iop1) = io1;
}
Expand All @@ -1735,17 +1754,16 @@ NPY_NO_EXPORT void
@type@ in1 = *(@type@ *)ip1;
const @type@ in2 = *(@type@ *)ip2;
/* Order of operations important for MSVC 2015 */
in1 = (in1 @OP@ in2 || npy_isnan(in1)) ? in1 : in2;
in1 = (@comp@(in1, in2) || npy_isnan(in1)) ? in1 : in2;
*((@type@ *)op1) = in1;
}
}
npy_clear_floatstatus_barrier((char*)dimensions);
}
/**end repeat1**/

/**begin repeat1
* #kind = fmax, fmin#
* #OP = >=, <=#
* #comp = isgreaterequal, islessequal#
**/
NPY_NO_EXPORT void
@TYPE@_@kind@(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func))
Expand All @@ -1755,7 +1773,7 @@ NPY_NO_EXPORT void
BINARY_REDUCE_LOOP(@type@) {
const @type@ in2 = *(@type@ *)ip2;
/* Order of operations important for MSVC 2015 */
io1 = (io1 @OP@ in2 || npy_isnan(in2)) ? io1 : in2;
io1 = (@comp@(io1, in2) || npy_isnan(in2)) ? io1 : in2;
}
*((@type@ *)iop1) = io1;
}
Expand All @@ -1764,10 +1782,9 @@ NPY_NO_EXPORT void
const @type@ in1 = *(@type@ *)ip1;
const @type@ in2 = *(@type@ *)ip2;
/* Order of operations important for MSVC 2015 */
*((@type@ *)op1) = (in1 @OP@ in2 || npy_isnan(in2)) ? in1 : in2;
*((@type@ *)op1) = (@comp@(in1, in2) || npy_isnan(in2)) ? in1 : in2;
}
}
npy_clear_floatstatus_barrier((char*)dimensions);
}
/**end repeat1**/

Expand Down Expand Up @@ -1844,9 +1861,9 @@ NPY_NO_EXPORT void
/* Sign of nan is nan */
UNARY_LOOP {
const @type@ in1 = *(@type@ *)ip1;
*((@type@ *)op1) = in1 > 0 ? 1 : (in1 < 0 ? -1 : (in1 == 0 ? 0 : in1));
*((@type@ *)op1) = isgreater(in1, 0) ? 1 : (
isless(in1, 0) ? -1 : (in1 == 0 ? 0 : in1));
}
npy_clear_floatstatus_barrier((char*)dimensions);
}

NPY_NO_EXPORT void
Expand Down Expand Up @@ -1942,11 +1959,10 @@ LONGDOUBLE_absolute(char **args, npy_intp const *dimensions, npy_intp const *ste
{
UNARY_LOOP {
const npy_longdouble in1 = *(npy_longdouble *)ip1;
const npy_longdouble tmp = in1 > 0 ? in1 : -in1;
const npy_longdouble tmp = isgreater(in1, 0) ? in1 : -in1;
/* add 0 to clear -0.0 */
*((npy_longdouble *)op1) = tmp + 0;
}
npy_clear_floatstatus_barrier((char*)dimensions);
}

NPY_NO_EXPORT void
Expand Down Expand Up @@ -2068,7 +2084,6 @@ HALF_@kind@(char **args, npy_intp const *dimensions, npy_intp const *steps, void
const npy_half in1 = *(npy_half *)ip1;
*((npy_bool *)op1) = @func@(in1) != 0;
}
npy_clear_floatstatus_barrier((char*)dimensions);
}
/**end repeat**/

Expand Down Expand Up @@ -2313,14 +2328,18 @@ HALF_ldexp_long(char **args, npy_intp const *dimensions, npy_intp const *steps,
*****************************************************************************
*/

#define CGE(xr,xi,yr,yi) ((xr > yr && !npy_isnan(xi) && !npy_isnan(yi)) \
|| (xr == yr && xi >= yi))
#define CLE(xr,xi,yr,yi) ((xr < yr && !npy_isnan(xi) && !npy_isnan(yi)) \
|| (xr == yr && xi <= yi))
#define CGT(xr,xi,yr,yi) ((xr > yr && !npy_isnan(xi) && !npy_isnan(yi)) \
|| (xr == yr && xi > yi))
#define CLT(xr,xi,yr,yi) ((xr < yr && !npy_isnan(xi) && !npy_isnan(yi)) \
|| (xr == yr && xi < yi))
#define CGE(xr,xi,yr,yi) ((isgreater(xr, yr) \
&& !npy_isnan(xi) && !npy_isnan(yi)) \
|| (xr == yr && isgreaterequal(xi, yi)))
#define CLE(xr,xi,yr,yi) ((isless(xr, yr) \
&& !npy_isnan(xi) && !npy_isnan(yi)) \
|| (xr == yr && islessequal(xi, yi)))
#define CGT(xr,xi,yr,yi) ((isgreater(xr, yr) \
&& !npy_isnan(xi) && !npy_isnan(yi)) \
|| (xr == yr && isgreater(xi, yi)))
#define CLT(xr,xi,yr,yi) ((isless(xr, yr) \
&& !npy_isnan(xi) && !npy_isnan(yi)) \
|| (xr == yr && isless(xi, yi)))
#define CEQ(xr,xi,yr,yi) (xr == yr && xi == yi)
#define CNE(xr,xi,yr,yi) (xr != yr || xi != yi)

Expand Down Expand Up @@ -2488,7 +2507,6 @@ NPY_NO_EXPORT void
const @ftype@ in1i = ((@ftype@ *)ip1)[1];
*((npy_bool *)op1) = @func@(in1r) @OP@ @func@(in1i);
}
npy_clear_floatstatus_barrier((char*)dimensions);
}
/**end repeat1**/

Expand Down Expand Up @@ -2610,7 +2628,6 @@ NPY_NO_EXPORT void
((@ftype@ *)op1)[0] = in1r;
((@ftype@ *)op1)[1] = in1i;
}
npy_clear_floatstatus_barrier((char*)dimensions);
}
/**end repeat1**/

Expand All @@ -2635,7 +2652,6 @@ NPY_NO_EXPORT void
((@ftype@ *)op1)[1] = in2i;
}
}
npy_clear_floatstatus_barrier((char*)dimensions);
}
/**end repeat1**/

Expand Down
13 changes: 3 additions & 10 deletions numpy/core/src/umath/loops_unary_fp.dispatch.c.src
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ NPY_FINLINE float c_recip_f32(float a)
{ return 1.0f / a; }
NPY_FINLINE float c_abs_f32(float a)
{
const float tmp = a > 0 ? a : -a;
const float tmp = isgreater(a, 0) ? a : -a;
/* add 0 to clear -0.0 */
return tmp + 0;
}
Expand All @@ -36,7 +36,7 @@ NPY_FINLINE double c_recip_f64(double a)
{ return 1.0 / a; }
NPY_FINLINE double c_abs_f64(double a)
{
const double tmp = a > 0 ? a : -a;
const double tmp = isgreater(a, 0) ? a : -a;
/* add 0 to clear -0.0 */
return tmp + 0;
}
Expand Down Expand Up @@ -252,7 +252,6 @@ static void simd_@TYPE@_@kind@_@STYPE@_@DTYPE@
/**begin repeat1
* #kind = ceil, sqrt, absolute, square, reciprocal#
* #intr = ceil, sqrt, abs, square, recip#
* #clear = 0, 0, 1, 0, 0#
*/
NPY_NO_EXPORT void NPY_CPU_DISPATCH_CURFX(@TYPE@_@kind@)
(char **args, npy_intp const *dimensions, npy_intp const *steps, void *NPY_UNUSED(func))
Expand Down Expand Up @@ -283,7 +282,7 @@ NPY_NO_EXPORT void NPY_CPU_DISPATCH_CURFX(@TYPE@_@kind@)
} else {
simd_@TYPE@_@kind@_NCONTIG_NCONTIG(src, ssrc, dst, sdst, len);
}
goto clear;
return;
no_unroll:
#endif // @VCHK@
for (; len > 0; --len, src += src_step, dst += dst_step) {
Expand All @@ -295,12 +294,6 @@ no_unroll:
*(npyv_lanetype_@sfx@*)dst = c_@intr@_@sfx@(src0);
#endif
}
#if @VCHK@
clear:;
#endif
#if @clear@
npy_clear_floatstatus_barrier((char*)dimensions);
#endif
}
/**end repeat1**/
/**end repeat**/
Loading