Mail Archives: djgpp-workers/1997/11/05/12:46:32
I needed to make a program which sets errno and calls matherr when a
numeric error occurs. It turns out that this feat involves using some
undocumented features which hide inside libm. The patches below document
them.
DJ, should we maybe set the default libm operation to _POSIX_?
*** /dev/null Wed Nov 5 18:21:43 1997
--- src/libm/libm.txh Wed Nov 5 17:39:50 1997
***************
*** 0 ****
--- 1,144 ----
+ @c ----------------------------------------------------------------------
+ @node libm, math
+ @subheading Syntax
+
+ @example
+ #include <libm/math.h>
+
+ enum fdversion {fdlibm_ieee = -1, fdlibm_svid, fdlibm_xopen, fdlibm_posix};
+
+ #define _LIB_VERSION_TYPE enum fdversion
+ #define _LIB_VERSION _fdlib_version
+
+ extern _LIB_VERSION_TYPE _LIB_VERSION;
+
+ #define _IEEE_ fdlibm_ieee
+ #define _SVID_ fdlibm_svid
+ #define _XOPEN_ fdlibm_xopen
+ #define _POSIX_ fdlibm_posix
+
+ _LIB_VERSION_TYPE _LIB_VERSION = _IEEE_;
+ @end example
+
+ @subheading Description
+
+ The alternate math library, @file{libm.a}, provides more accurate
+ versions of mathematical functions than those included in the default
+ @file{libc.a} library. It also allows to create programs with
+ well-defined and standard-compliant behavior when numerical errors
+ occur. In contrast, the functions in the default @file{libc.a} will
+ usually generate FP exceptions in such cases, which will cause signal
+ @code{SIGFPE} to be delivered to the program. The default handler for
+ @code{SIGFPE} aborts the program. @xref{signal}.
+
+ To use the alternate math library with your program, you need to do the
+ following:
+
+ @itemize @bullet{}
+
+ @item
+ Include the header @code{<libm/math.h>}. Alternatively, you can include
+ @code{<math.h>} as usual and compile with @samp{-D_USE_LIBM_MATH_H}
+ option to @code{gcc}, which will cause it to use @file{libm/math.h}
+ instead of the default @file{math.h}. (The second possibility leaves
+ the source ANSI-compliant.)
+
+ @item
+ Set the global variable @code{_fdlib_version} to a value other than the
+ default @code{_IEEE_}. The possible values are listed and explained
+ below.
+
+ @item
+ At the beginning of your @code{main} function, set the FPU to a
+ predictable state by calling @code{_clear87} (@pxref{_clear87}) and
+ @code{_fpreset} (@pxref{_fpreset}) library functions, and then mask the
+ Invalid Operation exception bit in the FPU control word
+ (@pxref{_control87}). (Another possibility is to make these calls in a
+ function declared with @code{__attribute__((constructor))}, so it will
+ be called before @code{main}.)
+
+ @item
+ Link your program with the @file{libm.a} library, e.g. by specifying
+ @samp{-lm} on the link command line.
+
+ @end itemize
+
+ The functions in @file{libm.a} can emulate different standards. You can
+ select to which standard your program will comply by setting the global
+ variable @code{_fdlib_version} (or the macro @code{_LIB_VERSION} which
+ evaluates to it) to one of the values below. This will only effect the
+ behavior of the math functions when an error is signalled by the FPU.
+
+ @table @code{}
+
+ @item _IEEE_
+ The default value, specifies IEEE-compliant operation. In case of an
+ error, this version will immediately return whatever result is computed
+ by the FPU, and will @strong{not} set @code{errno}. This gives the
+ fastest code.
+
+ @item _POSIX_
+ In case of an error, this version will set @code{errno} to the
+ appropriate value (@code{EDOM} or @code{ERANGE}) and return to the
+ caller, without calling the @code{matherr} function (@pxref{matherr}).
+ This version should be used for maximum POSIX- and ANSI-compliance.
+
+ @item _SVID_
+ This version is compliant with the @dfn{System V Interface Definition}.
+ This is the slowest version. In case of an error, it calls the
+ @code{matherr} function (@pxref{matherr}), which can be customized to
+ the specific application needs. If @code{matherr} returns zero, a
+ message is printed to the standard error stream which states the name of
+ the function that generated the error and the error type, and
+ @code{errno} is set. If @code{matherr} returns non-zero, there will be
+ no message and @code{errno} will be left unaltered.
+
+ @item _XOPEN_
+ Complies to the X/Open specifications. It behaves exactly like
+ @code{_SVID_}, but it never prints an error message, even if
+ @code{matherr} returns zero.
+
+ @end table
+
+ @subheading Example
+
+ @example
+
+ /* Testing errno == EDOM after sqrt(-1).
+
+ !!! MUST compile with -lm !!! */
+
+ #include <assert.h>
+ #include <errno.h>
+ #include <stdio.h>
+ #include <libm/math.h> /* or #define _USE_LIBM_MATH_H and #include <math.h> */
+ #include <float.h>
+
+ /* Setting _LIB_VERSION to anything but _IEEE_ will turn on errno handling. */
+ _LIB_VERSION_TYPE _LIB_VERSION = _POSIX_;
+
+ int main (void)
+ @{
+ unsigned cw = EM_INVALID; /* mask the Invalid Operation exception */
+ unsigned cw_mask = EM_INVALID; /* only care about INVALID bit in cw */
+ unsigned old_cw;
+
+ /* Reset the FPU (possible previous FP problems). */
+ _clear87 ();
+ _fpreset ();
+
+ /* Set the FPU to not generate SIGFPE on Invalid Operation. */
+ old_cw = _control87 (cw, cw_mask);
+
+ /* Run the test. */
+ errno = 0;
+ assert(errno == 0);
+ sqrt(-1.0);
+ assert(errno == EDOM); /* this line should NOT cause the assertion to fail */
+
+ /* Restore previous FPU state. */
+ _control87 (old_cw, 0x0000FFFFU);
+ return(0);
+ @}
+
+ @end example
*** /dev/null Wed Nov 5 18:25:12 1997
--- src/libm/src/s_matherr.txh Wed Nov 5 18:11:32 1997
***************
*** 0 ****
--- 1,116 ----
+ @c ----------------------------------------------------------------------
+ @node matherr, math
+ @subheading Syntax
+
+ @example
+ #include <libm/math.h>
+
+ enum fdversion _fdlib_version = _SVID_;
+
+ int matherr(struct exception *exc);
+ @end example
+
+ @subheading Description
+
+ @code{matherr} is a user-definable handler for errors in math library
+ functions. It is only supported in the alternate math library (link
+ with @samp{-lm}), and will only be called if the global variable
+ @code{_fdlib_version} is set to either @code{_SVID_} or @code{_XOPEN_}
+ (@pxref{libm}). You also need to mask the Invalid Operation exception
+ in the x87 control word (@pxref{_control87}) or install a handler for
+ signal @code{SIGFPE} (@pxref{signal}), or else some exceptions will
+ generate @code{SIGFPE} and your program will be terminated before it
+ gets a chance to call @code{matherr}.
+
+ If the above conditions are met, every math function will call
+ @code{matherr} when a numerical exception is detected. The default
+ version of @code{matherr}, supplied with @file{libm.a}, does nothing and
+ returns zero (the @code{_SVID_} version will then print an error message
+ to the standard error stream and set @code{errno}).
+
+ This default behavior is inappropriate in some cases. For example, an
+ interactive program which runs in a windowed environment might want the
+ error message to go to a particular window, or pop up a dialog box; a
+ fault-tolerant program might want to fall back to backup procedures so
+ that meaningful results are returned to the application code, etc. In
+ such cases, you should include your own version of @code{matherr} in
+ your program.
+
+ @code{matherr} is called with a single argument @var{exc} which is a
+ pointer to a structure defined on @code{<libm/math.h>} like this:
+
+ @example
+ struct exception @{
+ int type;
+ char *name;
+ double arg1, arg2, retval;
+ @};
+ @end example
+
+ The member @code{type} is an integer code describing the type of
+ exception that has occured. It can be one of the following:
+
+ @table @code{}
+
+ @item DOMAIN
+ Argument(s) are outside the valid function domain (e.g.,
+ @code{log(-1)}).
+
+ @item SING
+ Argument(s) would result in a singularity (e.g., @code{log(0)}).
+
+ @item OVERFLOW
+ The result causes overflow, like in @code{exp(10000)}.
+
+ @item UNDERFLOW
+ The result causes underflow, like in @code{exp(-10000)}.
+
+ @item TLOSS
+ The result loses all significant digits, like in @code{sin(10e100)}.
+
+ @end table
+
+ These codes are defined on @code{<libm/math.h>}.
+
+ The member @code{name} points to the string that is the name of the
+ function which generated the exception. The members @code{arg1} and
+ @code{arg2} are the values of the arguments with which the function was
+ called (@code{arg2} is undefined if the function only accepts a single
+ argument). The member @code{retval} is set to the default value that
+ will be returned by the math library function; @code{matherr} can change
+ it to return a different value.
+
+ @subheading Return Value
+
+ @code{matherr} should return zero if it couldn't handle the exception,
+ or non-zero if the exception was handled.
+
+ If @code{matherr} returns zero, under @code{_SVID_} version an error
+ message is printed which includes the name of the function and the
+ exception type, and under @code{_SVID_} and @code{_XOPEN_} @code{errno}
+ is set to an appropriate value. If @code{matherr} returns non-zero, no
+ error message is printed and @code{errno} is left unchanged.
+
+ @subheading Example
+
+ @example
+ #include <libm/math.h>
+
+ int matherr(register struct exception *x)
+ @{
+ switch (x->type) @{
+ case DOMAIN:
+ /* change sqrt to return sqrt(-arg1), not NaN */
+ if (!strcmp(x->name, "sqrt")) @{
+ x->retval = sqrt(-x->arg1);
+ return 1; /* be silent: no message, don't set errno */
+ @} /* FALL THROUGH */
+ case SING:
+ /* all other domain or sing exceptions, print message and abort */
+ fprintf(stderr, "domain exception in %s\n", x->name);
+ abort();
+ break;
+ @}
+ return 0; /* all other exceptions, execute default procedure */
+ @}
+ @end example
- Raw text -