Home > Back-end >  math functions detect errors
math functions detect errors

Time:09-07

I'm writing a program where the user can execute math.c functions at runtime. I'm tryng to detect the errors that the user can generate. After a bit research I come out with the global variable errno . It seems that if is 0 the last math function had no problem, otherwise there was a problem. I don't care which kind of error generated but I need just to know that the result is good or less. So I tried with acos() and if the argument is out the range [-1,1] it set the errno to a value different then 0. So I would know if I can trust this logic or there is some behavior that it doesn't gives me the right result .

CodePudding user response:

Overview

The C standard does not absolutely require the math routines in the standard C library to report errors. If they do report errors via errno, you must set errno to zero before calling a math routine, because they only change errno when an error occurs; they do not reset it to zero when an error does not occur.

C 2018 7.12.1 classifies errors in the math routines as domain errors (the input point is outside the domain in which the function is defined), pole errors (the value of the mathematical function is infinite), and range errors. Range errors are either overflow errors (the value of the mathematical function is finite but beyond the range the floating-point type can represent) or underflow errors (the value of the mathematical function is so small it cannot be represented without extraordinary roundoff error). Per C 2018 7.12.1 6, implementations may treat underflow as an error (report it via errno or raise the “underflow” exception) or not (leave errno and not raise an exception). This is implementation-defined; an implementation must define whether it sets errno and/or raises exceptions for underflow conditions.

There are a few things you can rely on:

  • If no error occurs, errno will not be changed. (C 2018 7.12.1 7 says “… If no such error occurs, errno shall be left unmodified regardless of the setting of math_errhandling.) It should be noted this differs from C standard library routines generally, as C 2018 7.5 3 says “… The value of errno may be set to nonzero by a library function call whether or not there is an error, provided the use of errno is not documented in the description of the function in this document.”
  • If a domain error occurs, some implementation-defined value will be returned. The best practice (which you cannot rely on from the C standard alone) is to return a NaN (which implies best practice is for the floating-point format to support NaN representations). A NaN is a special “value” in the floating-point format that means “Not a Number.” You can test whether a floating-point object x is a NaN by using isnan(x) or x != x. (Because a NaN is not a number, it is not equal to anything, not even itself.) Also, best practice is not to return a NaN for anything other than a domain error. You can test whether NaNs are supported; per C 2018 7.12 5, the macro NAN is defined by <math.h> if and only if quiet NaNs are supported in the float type.
  • If a pole error or overflow error occurs, HUGE_VAL, HUGE_VALF, or HUGE_VALL will be returned, according to the return type, with the correct sign. If infinities are supported in the floating-point format, HUGE_VAL should be infinity, but the standard does not strictly require it.
  • If an underflow occurs, whether it is treated as an error or not, the value returned will be no greater in magnitude than the smallest positive normal number.

Per C 2018 7.12 9, <math.h> defines three macros math_errhandling, MATH_ERRNO, and MATH_ERREXCEPT to indicate what error reporting the implementation provides. If math_errhandling & MATH_ERRNO is non-zero, math routines set errno to a non-zero value when an error occurs. (Recall that underflow might not be treated as an error.) If math_errhandling & MATH_ERREXCEPT is non-zero, math routines raise a floating-point exception when an error occurs.

Using errno

If math_errhandling & MATH_ERRNO is non-zero, then the C standard requires math library routines to set errno to non-zero if and only if a domain error, pole error, or range error occurs. The standard says this in C 2018 7.12.1 paragraphs 2 through 6, which require that errno be set to non-zero if one of these errors occurs and math_errhandling & MATH_ERRNO is non-zero, and paragraph 7, which requires that errno not be changed if no such error occurs (regardless of the value of math_errhandling & MATH_ERRNO). For domain errors, errno is set to EDOM. For pole and range errors, it is set to ERANGE.

Note that, if no error occurs, the standard says that errno is unmodified, not that it is set to zero. Thus, to detect errors, a program must set errno to zero before calling a math routine. So, if math_errhandling & MATH_ERRNO is non-zero, this code prints “Error” if and only if an error occurs:

errno = 0;
y = acos(x);
if (errno)
    printf("Error");

Using Exception Flags

If math_errhandling & MATH_ERRHANDLING is non-zero, then the C standard requires math library routines to:

  • raise the “invalid” floating-point exception if and only if a domain error occurs,
  • raise the “divide by zero” floating-point exception if and only if a pole error occurs,
  • raise the “overflow” floating-point exception if and only if an overflow occurs, and
  • optionally (implementation-defined) raise the “underflow” floating-point exception if an underflow occurs.

C 2018 7.12.1 1 says the floating-point exceptions “invalid,” “divide by zero,” and “overflow” are not raised unless an error occurs, hence the “and only if” for those exceptions above. It does not impose this requirement for the “underflow” or “inexact” exceptions. It is quite common for math library routines to do a variety of calculations internally that often have inexact results, and hence raise the “inexact” exception, even if the final calculated result is exact. For example, pow(25, .5) may correctly calculate the exact result 5 even though its internal use of logarithms involves inexact intermediate values and raises the “inexact” exception.

Similarly, the “underflow” exception may be raised by internal calculations even when the final result is in the normal range. Since routines are allowed to raise “underflow,” the fact that it is raised by a math routine does not indicate the mathematical function underflowed.

As with errno, the math library routines do not clear floating-point exception flags. Thus, if you want to pinpoint which specific routine is raising an exception, you must either clear exception flags before calling a routine or clear them after each exception is raised or trap on the desired floating-point exceptions, if your C implementation supports that and the performance effect is acceptable.

Additional information on floating-point exceptions and flags is in C 2018 7.6.

  • Related