From 7be59f1787f6a900b8fe4a0013ba3e9f2b7cea95 Mon Sep 17 00:00:00 2001 From: Sebastian Berg Date: Tue, 14 Jun 2022 09:43:58 -0700 Subject: [PATCH] MAINT: Simplify FE_INVALID redefine logic slightly and copy to cpp This builds on the previous, but just redefines them in the C/C++ file since this is not a header and defining an undefined macro value should not be a problem locally. Also tweaks the comment a bit. --- numpy/core/src/npymath/ieee754.c.src | 78 +++++++++++----------------- numpy/core/src/npymath/ieee754.cpp | 24 +++++++++ 2 files changed, 54 insertions(+), 48 deletions(-) diff --git a/numpy/core/src/npymath/ieee754.c.src b/numpy/core/src/npymath/ieee754.c.src index 5751ee2dfe58..5d1ea3a699bf 100644 --- a/numpy/core/src/npymath/ieee754.c.src +++ b/numpy/core/src/npymath/ieee754.c.src @@ -575,52 +575,34 @@ int npy_get_floatstatus() { */ # include -// musl-libc defines all the fenv functions unconditionally (fesetexcept, -// feclearexcept, etc) but on unsupported platforms they are no-ops: -// https://git.musl-libc.org/cgit/musl/tree/src/fenv/fenv.c -// However, the platform is expected to only define flags like -// FE_OVERFLOW if they are supported. I haven't found an explanation -// of the design decision, but it seems to be aimed at fine-grained -// feature detection. musl-libc-test has code that uses fenv and wants -// it to silently decay to a no-op on platforms missing support. I -// copied their implementation of this behavior: -// http://nsz.repo.hu/git/?p=libc-test;a=blob;f=src/common/mtest.h;h=706c1ba23ea8989b17a2f72ed1a919e187c06b6a;hb=HEAD#l30 -#undef INEXACT -#undef INVALID -#undef DIVBYZERO -#undef UNDERFLOW -#undef OVERFLOW -#ifdef FE_INEXACT -#define INEXACT FE_INEXACT -#else -#define INEXACT 0 -#endif -#ifdef FE_INVALID -#define INVALID FE_INVALID -#else -#define INVALID 0 +/* + * According to the C99 standard FE_DIVBYZERO, etc. may not be provided when + * unsupported. In such cases NumPy will not report these correctly, but we + * should still allow compiling (whether tests pass or not). + * By defining them as 0 locally, we make them no-ops. Unlike these defines, + * for example `musl` still defines all of the functions (as no-ops): + * https://git.musl-libc.org/cgit/musl/tree/src/fenv/fenv.c + * and does similar replacement in its tests: + * http://nsz.repo.hu/git/?p=libc-test;a=blob;f=src/common/mtest.h;h=706c1ba23ea8989b17a2f72ed1a919e187c06b6a;hb=HEAD#l30 + */ +#ifndef FE_DIVBYZERO + #define FE_DIVBYZERO 0 #endif -#ifdef FE_DIVBYZERO -#define DIVBYZERO FE_DIVBYZERO -#else -#define DIVBYZERO 0 +#ifndef FE_OVERFLOW + #define FE_OVERFLOW 0 #endif -#ifdef FE_UNDERFLOW -#define UNDERFLOW FE_UNDERFLOW -#else -#define UNDERFLOW 0 +#ifndef FE_UNDERFLOW + #define FE_UNDERFLOW 0 #endif -#ifdef FE_OVERFLOW -#define OVERFLOW FE_OVERFLOW -#else -#define OVERFLOW 0 +#ifndef FE_INVALID + #define FE_INVALID 0 #endif int npy_get_floatstatus_barrier(char* param) { - int fpstatus = fetestexcept(DIVBYZERO | OVERFLOW | - UNDERFLOW | INVALID); + int fpstatus = fetestexcept(FE_DIVBYZERO | FE_OVERFLOW | + FE_UNDERFLOW | FE_INVALID); /* * By using a volatile, the compiler cannot reorder this call */ @@ -628,10 +610,10 @@ int npy_get_floatstatus_barrier(char* param) volatile char NPY_UNUSED(c) = *(char*)param; } - return ((DIVBYZERO & fpstatus) ? NPY_FPE_DIVIDEBYZERO : 0) | - ((OVERFLOW & fpstatus) ? NPY_FPE_OVERFLOW : 0) | - ((UNDERFLOW & fpstatus) ? NPY_FPE_UNDERFLOW : 0) | - ((INVALID & fpstatus) ? NPY_FPE_INVALID : 0); + return ((FE_DIVBYZERO & fpstatus) ? NPY_FPE_DIVIDEBYZERO : 0) | + ((FE_OVERFLOW & fpstatus) ? NPY_FPE_OVERFLOW : 0) | + ((FE_UNDERFLOW & fpstatus) ? NPY_FPE_UNDERFLOW : 0) | + ((FE_INVALID & fpstatus) ? NPY_FPE_INVALID : 0); } int npy_clear_floatstatus_barrier(char * param) @@ -639,8 +621,8 @@ int npy_clear_floatstatus_barrier(char * param) /* testing float status is 50-100 times faster than clearing on x86 */ int fpstatus = npy_get_floatstatus_barrier(param); if (fpstatus != 0) { - feclearexcept(DIVBYZERO | OVERFLOW | - UNDERFLOW | INVALID); + feclearexcept(FE_DIVBYZERO | FE_OVERFLOW | + FE_UNDERFLOW | FE_INVALID); } return fpstatus; @@ -649,21 +631,21 @@ int npy_clear_floatstatus_barrier(char * param) void npy_set_floatstatus_divbyzero(void) { - feraiseexcept(DIVBYZERO); + feraiseexcept(FE_DIVBYZERO); } void npy_set_floatstatus_overflow(void) { - feraiseexcept(OVERFLOW); + feraiseexcept(FE_OVERFLOW); } void npy_set_floatstatus_underflow(void) { - feraiseexcept(UNDERFLOW); + feraiseexcept(FE_UNDERFLOW); } void npy_set_floatstatus_invalid(void) { - feraiseexcept(INVALID); + feraiseexcept(FE_INVALID); } diff --git a/numpy/core/src/npymath/ieee754.cpp b/numpy/core/src/npymath/ieee754.cpp index 2244004c0267..27fcf7c6e8cf 100644 --- a/numpy/core/src/npymath/ieee754.cpp +++ b/numpy/core/src/npymath/ieee754.cpp @@ -655,6 +655,30 @@ npy_get_floatstatus() */ #include +/* + * According to the C99 standard FE_DIVBYZERO, etc. may not be provided when + * unsupported. In such cases NumPy will not report these correctly, but we + * should still allow compiling (whether tests pass or not). + * By defining them as 0 locally, we make them no-ops. Unlike these defines, + * for example `musl` still defines all of the functions (as no-ops): + * https://git.musl-libc.org/cgit/musl/tree/src/fenv/fenv.c + * and does similar replacement in its tests: + * http://nsz.repo.hu/git/?p=libc-test;a=blob;f=src/common/mtest.h;h=706c1ba23ea8989b17a2f72ed1a919e187c06b6a;hb=HEAD#l30 + */ +#ifndef FE_DIVBYZERO + #define FE_DIVBYZERO 0 +#endif +#ifndef FE_OVERFLOW + #define FE_OVERFLOW 0 +#endif +#ifndef FE_UNDERFLOW + #define FE_UNDERFLOW 0 +#endif +#ifndef FE_INVALID + #define FE_INVALID 0 +#endif + + extern "C" int npy_get_floatstatus_barrier(char *param) {