Skip to content

Commit

Permalink
MAINT: Simplify FE_INVALID redefine logic slightly and copy to cpp
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
seberg committed Jun 14, 2022
1 parent 3cb57d5 commit 7be59f1
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 48 deletions.
78 changes: 30 additions & 48 deletions numpy/core/src/npymath/ieee754.c.src
Expand Up @@ -575,72 +575,54 @@ int npy_get_floatstatus() {
*/
# include <fenv.h>

// 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
*/
if (param != NULL) {
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)
{
/* 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;
Expand All @@ -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);
}

24 changes: 24 additions & 0 deletions numpy/core/src/npymath/ieee754.cpp
Expand Up @@ -655,6 +655,30 @@ npy_get_floatstatus()
*/
#include <fenv.h>

/*
* 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)
{
Expand Down

0 comments on commit 7be59f1

Please sign in to comment.