A test program (attached) reveals the value of math_errhandling
and whether various operations behave like math_errhandling says.
The result is pretty sobering: math_errhandling is useless.


2024-07-19  Bruno Haible  <br...@clisp.org>

        doc: Add documentation about math_errhandling.
        * doc/posix-functions/math_errhandling.texi: New file.
        * doc/gnulib.texi (Function Substitutes): Include it.

diff --git a/doc/gnulib.texi b/doc/gnulib.texi
index f538925d8d..683d2aabca 100644
--- a/doc/gnulib.texi
+++ b/doc/gnulib.texi
@@ -1907,6 +1907,7 @@
 * lseek::
 * lstat::
 * malloc::
+* math_errhandling::
 * mblen::
 * mbrlen::
 * mbrtoc8::
@@ -3303,6 +3304,7 @@
 @include posix-functions/lseek.texi
 @include posix-functions/lstat.texi
 @include posix-functions/malloc.texi
+@include posix-functions/math_errhandling.texi
 @include posix-functions/mblen.texi
 @include posix-functions/mbrlen.texi
 @include posix-functions/mbrtoc8.texi
diff --git a/doc/posix-functions/math_errhandling.texi 
b/doc/posix-functions/math_errhandling.texi
new file mode 100644
index 0000000000..f897a5ec4b
--- /dev/null
+++ b/doc/posix-functions/math_errhandling.texi
@@ -0,0 +1,34 @@
+@node math_errhandling
+@section @code{math_errhandling}
+@findex math_errhandling
+
+ISO C23 specification:@* 
@url{http://www.open-std.org/jtc1/sc22/wg14/www/docs/n3096.pdf} section 7.12.1
+
+Documentation:@* 
@uref{https://www.kernel.org/doc/man-pages/online/pages/man7/math_error.7.html,,man
 math_error}
+
+Gnulib module: ---
+
+Portability problems fixed by Gnulib:
+@itemize
+@end itemize
+
+Portability problems not fixed by Gnulib:
+@itemize
+@item
+This macro is missing on some platforms:
+NetBSD 10.0, mingw.
+@item
+This macro does not describe the error behaviour
+of elementary arithmetic operations (+, -, *, /)
+and of mathematical operations for which the compiler emits inline code
+(such as @code{sqrt} on some CPUs).
+@item
+This macro does not describe the error behaviour of functions
+such as @code{strtod}.
+@item
+For mathematical operations in general, it is a safer bet to look
+at the exceptions set in the floating-point environment
+(by calling @code{feclearexcept (FE_ALL_EXCEPT)} before the operation
+and @code{fetestexcept} after the operation),
+regardless of @code{math_errhandling}.
+@end itemize
/* Test error handling of math functions.
   https://man7.org/linux/man-pages/man7/math_error.7.html
   https://en.cppreference.com/w/cpp/numeric/math/math_errhandling
 */
#include <errno.h>
#include <fenv.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>

#ifndef MATH_ERRNO
# define MATH_ERRNO 1
#endif
#ifndef MATH_ERREXCEPT
# define MATH_ERREXCEPT 2
#endif
#ifndef math_errhandling
# define math_errhandling 0
#endif

volatile double z;

int main ()
{
  printf ("math_errhandling    = %d %d\n", (math_errhandling & MATH_ERREXCEPT) ? 1 : 0, (math_errhandling & MATH_ERRNO) ? 1 : 0);

  {
    errno = 0;
    feclearexcept (FE_ALL_EXCEPT);
    char *end;
    z = strtod ("1e500", &end);
    int err = errno;
    printf ("strtod overflow     = %d %d\n", fetestexcept (FE_OVERFLOW) ? 1 : 0, err == ERANGE);
  }

  {
    errno = 0;
    feclearexcept (FE_ALL_EXCEPT);
    char *end;
    z = strtod ("1e-320", &end);
    int err = errno;
    printf ("strtod underflow    = %d %d\n", fetestexcept (FE_UNDERFLOW) ? 1 : 0, err == ERANGE);
  }

  {
    errno = 0;
    feclearexcept (FE_ALL_EXCEPT);
    volatile double x = 1.0;
    volatile double y = 0.0;
    z = x / y;
    int err = errno;
    printf ("Division pole error = %d %d\n", fetestexcept (FE_DIVBYZERO) ? 1 : 0, err == ERANGE);
  }

  {
    errno = 0;
    feclearexcept (FE_ALL_EXCEPT);
    volatile double x = 1.0e300;
    volatile double y = 1.0e-200;
    z = x / y;
    int err = errno;
    printf ("Division overflow   = %d %d\n", fetestexcept (FE_OVERFLOW) ? 1 : 0, err == ERANGE);
  }

  {
    errno = 0;
    feclearexcept (FE_ALL_EXCEPT);
    volatile double x = 1.0e-300;
    volatile double y = 1.0e20;
    z = x / y;
    int err = errno;
    printf ("Division underflow  = %d %d\n", fetestexcept (FE_UNDERFLOW) ? 1 : 0, err == ERANGE);
  }

  {
    errno = 0;
    feclearexcept (FE_ALL_EXCEPT);
    volatile double x = 1.0;
    volatile double y = 10.0;
    z = x / y;
    int err = errno;
    printf ("Division inexact    = %d %d\n", fetestexcept (FE_INEXACT) ? 1 : 0, err != 0);
  }

  {
    errno = 0;
    feclearexcept (FE_ALL_EXCEPT);
    volatile double x = 2.0;
    z = sqrt (x);
    int err = errno;
    printf ("sqrt inexact        = %d %d\n", fetestexcept (FE_INEXACT) ? 1 : 0, err != 0);
  }

  {
    errno = 0;
    feclearexcept (FE_ALL_EXCEPT);
    volatile double x = 1.0e300;
    volatile double y = 2.0;
    z = pow (x, y);
    int err = errno;
    printf ("pow overflow        = %d %d\n", fetestexcept (FE_OVERFLOW) ? 1 : 0, err == ERANGE);
  }

  {
    errno = 0;
    feclearexcept (FE_ALL_EXCEPT);
    volatile double x = 1.0e-300;
    volatile double y = 2.0;
    z = pow (x, y);
    int err = errno;
    printf ("pow underflow       = %d %d\n", fetestexcept (FE_UNDERFLOW) ? 1 : 0, err == ERANGE);
  }

  {
    errno = 0;
    feclearexcept (FE_ALL_EXCEPT);
    volatile double x = 0.0;
    z = log (x);
    int err = errno;
    printf ("Log pole error      = %d %d\n", fetestexcept (FE_DIVBYZERO) ? 1 : 0, err == ERANGE);
  }

  {
    errno = 0;
    feclearexcept (FE_ALL_EXCEPT);
    volatile double x = 2.0;
    z = asin (x);
    int err = errno;
    printf ("Trig domain error   = %d %d\n", fetestexcept (FE_INVALID) ? 1 : 0, err == EDOM);
  }

  return 0;
}

/*
 * gcc -Wall foo.c -lm
 */

/*
glibc/x86_64:
math_errhandling    = 1 1
strtod overflow     = 1 1
strtod underflow    = 1 1
Division pole error = 1 0
Division overflow   = 1 0
Division underflow  = 1 0
Division inexact    = 1 0
sqrt inexact        = 1 0
pow overflow        = 1 1
pow underflow       = 1 1
Log pole error      = 1 1
Trig domain error   = 1 1

musl libc/x86_64:
math_errhandling    = 1 0
strtod overflow     = 1 1
strtod underflow    = 0 1
Division pole error = 1 0
Division overflow   = 1 0
Division underflow  = 1 0
Division inexact    = 1 0
sqrt inexact        = 1 0
pow overflow        = 1 0
pow underflow       = 1 0
Log pole error      = 1 0
Trig domain error   = 1 0

macOS 12/arm64, FreeBSD/x86_64, OpenBSD/x86_64, Android/arm:
math_errhandling    = 1 0
strtod overflow     = 0 1
strtod underflow    = 0 1
Division pole error = 1 0
Division overflow   = 1 0
Division underflow  = 1 0
Division inexact    = 1 0
sqrt inexact        = 1 0
pow overflow        = 1 0
pow underflow       = 1 0
Log pole error      = 1 0
Trig domain error   = 1 0

NetBSD/x86_64:
math_errhandling    = 0 0
strtod overflow     = 0 1
strtod underflow    = 0 1
Division pole error = 1 0
Division overflow   = 1 0
Division underflow  = 1 0
Division inexact    = 1 0
sqrt inexact        = 1 0
pow overflow        = 1 1
pow underflow       = 1 1
Log pole error      = 1 1
Trig domain error   = 1 1

AIX/powerpc64:
math_errhandling    = 0 1
strtod overflow     = 1 1
strtod underflow    = 1 1
Division pole error = 1 0
Division overflow   = 1 0
Division underflow  = 1 0
Division inexact    = 1 0
sqrt inexact        = 1 0
pow overflow        = 1 1
pow underflow       = 1 1
Log pole error      = 1 1
Trig domain error   = 1 1

Solaris 10/x86_64, Solaris 10/sparc, Solaris 11/x86_64:
math_errhandling    = 1 0
strtod overflow     = 1 1
strtod underflow    = 1 1
Division pole error = 1 0
Division overflow   = 1 0
Division underflow  = 1 0
Division inexact    = 1 0
sqrt inexact        = 1 0
pow overflow        = 1 1
pow underflow       = 1 1
Log pole error      = 1 1
Trig domain error   = 1 1

Cygwin/x86_64:
math_errhandling    = 1 1
strtod overflow     = 0 1
strtod underflow    = 0 1
Division pole error = 1 0
Division overflow   = 1 0
Division underflow  = 1 0
Division inexact    = 1 0
sqrt inexact        = 1 0
pow overflow        = 1 1
pow underflow       = 1 1
Log pole error      = 1 1
Trig domain error   = 1 1

mingw/x86_64:
math_errhandling    = 0 0
strtod overflow     = 0 1
strtod underflow    = 0 1
Division pole error = 1 0
Division overflow   = 1 0
Division underflow  = 1 0
Division inexact    = 1 0
sqrt inexact        = 1 0
pow overflow        = 1 0
pow underflow       = 1 0
Log pole error      = 0 1
Trig domain error   = 1 1

MSVC/x86_64:
math_errhandling    = 1 1
strtod overflow     = 0 1
strtod underflow    = 0 0
Division pole error = 1 0
Division overflow   = 1 0
Division underflow  = 1 0
Division inexact    = 1 0
sqrt inexact        = 1 0
pow overflow        = 1 1
pow underflow       = 1 0
Log pole error      = 1 1
Trig domain error   = 1 1

=== Summary ===
Division *          = 1 0  because that's what the compiler does (libc not relevant)
sqrt inexact        = 1 0  because that's what the compiler does
strtod overflow     = 0 1 or 1 1  apparently traditional behaviour from before fenv.h existed
strtod underflow    likewise, except on MSVC which does not report it at all
pow*, Trig *        = 1 0 or 1 1  apparently some platform want to avoid the cost of setting errno
Log pole error      likewise, except on mingw (= 0 1)
math_errhandling    = 1 0 or 1 1  (reasonable)
                      except on NetBSD/x86_64, where it is = 0 0 but should be = 1 1
                      except on mingw/x86_64, where it is = 0 0 but should be = 1 0
                      except on AIX/powerpc64, where it is = 0 1 but should be = 1 1

*/

Reply via email to