views:

451

answers:

6

Hi,

I am not sure how to deal with floating point exceptions in either C or C++. From wiki, there are following types of floating point exceptions:

IEEE 754 specifies five arithmetic errors that are to be recorded in "sticky bits" (by default; note that trapping and other alternatives are optional and, if provided, non-default).  

* inexact, set if the rounded (and returned) value is different from the mathematically exact result of the operation.  
* underflow, set if the rounded value is tiny (as specified in IEEE 754) and inexact (or maybe limited to if it has denormalisation loss, as per the 1984 version of IEEE 754), returning a subnormal value (including the zeroes).  
* overflow, set if the absolute value of the rounded value is too large to be represented (an infinity or maximal finite value is returned, depending on which rounding is used).  
* divide-by-zero, set if the result is infinite given finite operands (returning an infinity, either +∞ or −∞).  
* invalid, set if a real-valued result cannot be returned (like for sqrt(−1), or 0/0), returning a quiet NaN.

Is it that when any type of above exceptions happens, the program will exit abnormally? Or the program will carry this error on without mentioning anything and therefore make the error hard to debug?

Is a compiler like gcc able to give warning for some obvious case?

What can I do during coding my program to notify where the error happens and what types it is when it happens, so that I can locate the error easily in my code? Please give solutions in both C and C++ case.

Thanks and regards!

+3  A: 

There are many options, but the general and also the default philosophy introduced by 754 is to not trap but to instead produce special results such as infinities that may or may not show up in important results.

As a result, the functions that test the state of individual operations are not used as often as the functions that test the representations of results.

See, for example...

LIST OF FUNCTIONS

 Each of the functions that use floating-point values are provided in sin-
 gle, double, and extended precision; the double precision prototypes are
 listed here.  The man pages for the individual functions provide more
 details on their use, special cases, and prototypes for their single and
 extended precision versions.

 int fpclassify(double)
 int isfinite(double)
 int isinf(double)
 int isnan(double)
 int isnormal(double)
 int signbit(double)

Update: For anyone who really thinks FPU ops generate SIGFPE in a default case these days, I would encourage you to try this program. You can easily generate underflow, overflow, and divide-by-zero. What you will not generate (unless you run it on the last surviving VAX or a non-754 RISC) is SIGFPE:

#include <stdio.h>
#include <stdlib.h>
int main(int ac, char **av) { return printf("%f\n", atof(av[1]) / atof(av[2])); }
DigitalRoss
I should add that you do not care about many of the sticky bits anyway. Underflow is rare and about the same thing as zero. Inexact is too common to care about.
DigitalRoss
Thanks! To test the result of every expression seems too much to handle in the code. Even with tests for some results deemed to be important, it is still very likely that the results without tests will throw out exceptions. All I hope to do is to catch the exception to report where it happens and what type it is.
Tim
There won't be an exception and you don't have to test after operations. C99 did introduce functions to test the FPU sticky bits but they are still not universally available and even if they were you wouldn't want to waste the time.
DigitalRoss
+1  A: 

Different compilers handle these errors in different ways.

Inexactness is almost always the result of division of numbers with an absolute value greater than one (perhaps through trancendental functions). Adding, subtracting and multiplying numbers with an absolute value > 1.0 can only result in overflow.

Underflow doesn't occur very often, and probably won't be a concern in normal calculations except for iterated functions such as Taylor series.

Overflow is a problem that can usually be detected by some sort of "infinity" comparison, different compilers vary.

Divide by zero is quite noticable since your program will (should) crash if you don't have an error handler. Checking dividends and divisors will help avoid the problem.

Invalid answers usually are caught without special error handlers with some sort of DOMAIN error printed.

[EDIT]

This might help: (Numerical Computation Guide by Sun) http://docs.sun.com/source/806-3568/

Arthur Kalliokoski
A: 

In Linux, you can trap these exceptions by trapping the SIGFPE signal. If you do nothing, these exceptions will terminate your program. To set a handler, use the signal function, passing the signal you wish to have trapped, and the function to be called in the event the signal fires.

sizzzzlerz
Thanks! Is it possible to know and print out where the exception occurs in the function that handles the signal?
Tim
This isn't true. In general, only pre-754 systems generate SIGFPE for individual operations. Also, things like inexact and underflow have never ever been exceptions. This program can be made to generate x/0, overflow, and underflow. It won't generate a SIGFPE. `#include <stdio.h>#include <stdlib.h>int main(int ac, char **av) { return printf("%f\n", atof(av[1]) / atof(av[2])); }`
DigitalRoss
+1  A: 

On Linux you can use the GNU extension feenableexcept (hidden right at the bottom of that page) to turn on trapping on floating point exceptions - if you do this then you'll receive the signal SIGFPE when an exception occurs which you can then catch in your debugger. Watch out though as sometimes the signal gets thrown on the floating point instruction after the one that's actually causing the problem, giving misleading line information in the debugger!

Mike Dinsdale
Thanks, Mike! if without calling feenableexcept, is it impossible for SIGFPE to be trap? If only using C standard library to specify a handler for SIGFPE through signal() without calling feenableexcept in GNU extension, will it make my program receive SIGFPE?
Tim
+1  A: 

C99 introduced functions for handling floating point exceptions. Prior to a floating point operation, you can use feclearexcept() to clear any outstanding exceptions. After the operation(s), you can then use fetestexcept() to test which exception flags are set.

caf
+1  A: 

On Windows with Visual C++, you can control which floating-point exceptions are unmasked using _control87() etc.. Unmasked floating-point exceptions generate structured exceptions, which can be handled using __try/__except (and a couple of other mechanisms). This is all completely platform-dependent.

If you leave floating point exceptions masked, another platform-dependent approach to detecting these conditions is to clear the floating-point status using _clear87() etc., perform computations, and then query the floating-point status using _status87() etc..

Is any of this any better than DigitalRoss's suggestion of checking the result? In most cases, it's not. If you need to detect (or control) rounding (which is unlikely), then maybe?

On Windows with Borland/CodeGear/Embarcadero C++, some floating-point exceptions are unmasked by default, which often causes problems when using third-party libraries that were not tested with floating-point exceptions unmasked.

bk1e