IEEE floating-point exceptions in C++
2013-07-25 09:23
225 查看
This page will answer the following questions.
My program just printed out
How can I tell if a number is really a number and not a
How can I find out more details at runtime about kinds of
Do you have any sample code to show how this works?
Where can I learn more?
These questions have to do with floating point exceptions. If you get some strange non-numeric output where you're expecting a number, you've either exceeded the finite limits of floating point arithmetic or you've asked for some result that is undefined.
To keep things simple, I'll stick to working with the
if the result would be a negative number too large to store in a
demonstrate some operations that produce infinities.
Some operations don't make mathematical sense, such as taking the square root of a negative number. (Yes, this operation makes sense in the context of complex numbers, but a
to represent the result.) The same is true for logarithms of negative numbers. Both
as
In short, if you get
is difficult to compute, seeAvoiding Overflow, Underflow, and Loss of Precision. That article gives tricks for computing results that have intermediate steps overflow if computed directly.
are simple, portable ways to get summary information and more complicated, less portableways to get more information.
First, the simple solution. If you want to test whether a
do not. I've used this trick on Windows, Linux, and Mac OSX.If you ever use this trick, put big bold comments around your code so that some well-meaningperson won't come behind you and delete what he or she things is useless code.Better yet, put the test in
a well-documented function in a library that has controlled access. The following function will test whether
To test whether a variable contains a finite number, (i.e. not a
Here
is not a
to what I've tested and just talk about Windows from here on out.
The Windows function
The following code illustrates which kinds of operations result in which kinds of numbers. To port this code to Linux, the
To learn more
For a brief explanation of numerical limits and how floating point numbers are laid out in memory, seeAnatomy of a floating point number.
For much more detail regarding exceptions and IEEE arithmetic in general, see
What every computer scientist should know about floating-point arithmetic.
From John D. Cook's blog
My program just printed out
1.#INDor
1.#INF(on Windows) or
nanor
inf(on Linux). What happened?
How can I tell if a number is really a number and not a
NaNor an infinity?
How can I find out more details at runtime about kinds of
NaNs and infinities?
Do you have any sample code to show how this works?
Where can I learn more?
These questions have to do with floating point exceptions. If you get some strange non-numeric output where you're expecting a number, you've either exceeded the finite limits of floating point arithmetic or you've asked for some result that is undefined.
To keep things simple, I'll stick to working with the
doublefloating point type. Similar remarks hold for
floattypes.
Debugging 1.#IND, 1.#INF, nan, and inf
If your operation would generate a larger positive number than could be stored in adouble, the operation will return
1.#INFon Windows or
infon Linux. Similarly your code will return
-1.#INFor
-inf
if the result would be a negative number too large to store in a
double. Dividing a positive number by zero produces a positive infinity and dividing a negative number by zero produces a negative infinity. Example code at the end of this page will
demonstrate some operations that produce infinities.
Some operations don't make mathematical sense, such as taking the square root of a negative number. (Yes, this operation makes sense in the context of complex numbers, but a
doublerepresents a real number and so there is no
double
to represent the result.) The same is true for logarithms of negative numbers. Both
sqrt(-1.0)and
log(-1.0)would return a
NaN, the generic term for a "number" that is "not a number". Windows displays a
NaN
as
-1.#IND("IND" for "indeterminate") while Linux displays
nan. Other operations that would return a
NaNinclude 0/0, 0*∞, and ∞/∞. See the sample code below for examples.
In short, if you get
1.#INFor
inf, look for overflow or division by zero. If you get
1.#INDor
nan, look for illegal operations. Maybe you simply have a bug. If it's more subtle and you have something that
is difficult to compute, seeAvoiding Overflow, Underflow, and Loss of Precision. That article gives tricks for computing results that have intermediate steps overflow if computed directly.
Testing for NaNs and infinities
Next suppose you want to test whether a number is an infinity or aNaN.For example, you may want to write to a log file print a debug message when a numericalresult goes bad, or you may want to execute some sort of alternate logic in your code.There
are simple, portable ways to get summary information and more complicated, less portableways to get more information.
First, the simple solution. If you want to test whether a
doublevariable contains a valid number, you can check whether
x == x.This looks like it should always be true, but it's not! Ordinary numbers alwaysequal themselves, but
NaNs
do not. I've used this trick on Windows, Linux, and Mac OSX.If you ever use this trick, put big bold comments around your code so that some well-meaningperson won't come behind you and delete what he or she things is useless code.Better yet, put the test in
a well-documented function in a library that has controlled access. The following function will test whether
xis a (possibly infinite) number.
bool IsNumber(double x) { // This looks like it should always be true, // but it's false if x is a NaN. return (x == x); }
To test whether a variable contains a finite number, (i.e. not a
NaNand not an infinity) you can use code like the following.
bool IsFiniteNumber(double x) { return (x <= DBL_MAX && x >= -DBL_MAX); }
Here
DBL_MAXis a constant defined in
float.has the largest
doublethat can be represented. Comparisons with
NaNs always fail,even when comparing to themselves, and so the test above will fail for a
NaN.If
x
is not a
NaNbut is infinite, one of the two tests will fail depending on whether it is a positive infinity or negative infinity.
Getting more information programmatically
To get more detail about the type of a floating point number, there is a function_fpclasson Windows and a corresponding function
fp_class_don Linux. I have not been able to get the corresponding Linux code to workand so I'll stick
to what I've tested and just talk about Windows from here on out.
The Windows function
_fpclassreturns one of the following values:
_FPCLASS_SNAN // signaling NaN _FPCLASS_QNAN // quiet NaN _FPCLASS_NINF // negative infinity _FPCLASS_NN // negative normal _FPCLASS_ND // negative denormal _FPCLASS_NZ // -0 _FPCLASS_PZ // +0 _FPCLASS_PD // positive denormal _FPCLASS_PN // positive normal _FPCLASS_PINF // positive infinity
The following code illustrates which kinds of operations result in which kinds of numbers. To port this code to Linux, the
FPClassfunction would need to use
fp_class_dand its corresponding constants.
#include <cfloat> #include <iostream> #include <sstream> #include <cmath> using namespace std; string FPClass(double x) { int i = _fpclass(x); string s; switch (i) { case _FPCLASS_SNAN: s = "Signaling NaN"; break; case _FPCLASS_QNAN: s = "Quiet NaN"; break; case _FPCLASS_NINF: s = "Negative infinity (-INF)"; break; case _FPCLASS_NN: s = "Negative normalized non-zero"; break; case _FPCLASS_ND: s = "Negative denormalized"; break; case _FPCLASS_NZ: s = "Negative zero (-0)"; break; case _FPCLASS_PZ: s = "Positive 0 (+0)"; break; case _FPCLASS_PD: s = "Positive denormalized"; break; case _FPCLASS_PN: s = "Positive normalized non-zero"; break; case _FPCLASS_PINF: s = "Positive infinity (+INF)"; break; } return s; } string HexDump(double x) { unsigned long* pu; pu = (unsigned long*)&x; ostringstream os; os << hex << pu[0] << " " << pu[1]; return os.str(); } // ---------------------------------------------------------------------------- int main() { double x, y, z; cout << "Testing z = 1/0\n"; // cannot set x = 1/0 directly or would produce compile error. x = 1.0; y = 0; z = x/y; cout << "z = " << x/y << "\n"; cout << HexDump(z) << " _fpclass(z) = " << FPClass(z) << "\n"; cout << "\nTesting z = -1/0\n"; x = -1.0; y = 0; z = x/y; cout << "z = " << x/y << "\n"; cout << HexDump(z) << " _fpclass(z) = " << FPClass(z) << "\n"; cout << "\nTesting z = sqrt(-1)\n"; x = -1.0; z = sqrt(x); cout << "z = " << z << "\n"; cout << HexDump(z) << " _fpclass(z) = " << FPClass(z) << "\n"; cout << "\nTesting z = log(-1)\n"; x = -1.0; z = sqrt(x); cout << "z = " << z << "\n"; cout << HexDump(z) << " _fpclass(z) = " << FPClass(z) << "\n"; cout << "\nTesting overflow\n"; z = DBL_MAX; cout << "z = DBL_MAX = " << z; z *= 2.0; cout << "; 2z = " << z << "\n"; cout << HexDump(z) << " _fpclass(z) = " << FPClass(z) << "\n"; cout << "\nTesting denormalized underflow\n"; z = DBL_MIN; cout << "z = DBL_MIN = " << z << "\n"; cout << HexDump(z) << " _fpclass(z) = " << FPClass(z) << "\n"; z /= pow(2.0, 52); cout << "z = DBL_MIN / 2^52= " << z << "\n"; cout << HexDump(z) << " _fpclass(z) = " << FP 4000 Class(z) << "\n"; z /= 2; cout << "z = DBL_MIN / 2^53= " << z << "\n"; cout << HexDump(z) << " _fpclass(z) = " << FPClass(z) << "\n"; cout << "\nTesting z = +infinity + -infinty\n"; x = 1.0; y = 0.0; x /= y; y = -x; cout << x << " + " << y << " = " << z << "\n"; cout << HexDump(z) << " _fpclass(z) = " << FPClass(z) << "\n"; cout << "\nTesting z = 0 * infinity\n"; x = 1.0; y = 0.0; x /= y; z = 0.0*x; cout << "x = " << x << "; z = 0*x = " << z << "\n"; cout << HexDump(z) << " _fpclass(z) = " << FPClass(z) << "\n"; cout << "\nTesting 0/0\n"; x = 0.0; y = 0.0; z = x/y; cout << "z = 0/0 = " << z << "\n"; cout << HexDump(z) << " _fpclass(z) = " << FPClass(z) << "\n"; cout << "\nTesting z = infinity/infinity\n"; x = 1.0; y = 0.0; x /= y; y = x; z = x/y; cout << "x = " << x << "; z = x/x = " << z << "\n"; cout << HexDump(z) << " _fpclass(z) = " << FPClass(z) << "\n"; cout << "\nTesting x fmod 0\n"; x = 1.0; y = 0.0; z = fmod(x, y); cout << "fmod(" << x << ", " << y << ") = " << z << "\n"; cout << HexDump(z) << " _fpclass(z) = " << FPClass(z) << "\n"; cout << "\nTesting infinity fmod x\n"; y = 1.0; x = 0.0; y /= x; z = fmod(y, x); cout << "fmod(" << y << ", " << x << ") = " << z << "\n"; cout << HexDump(z) << " _fpclass(z) = " << FPClass(z) << "\n"; cout << "\nGetting cout to print QNAN\n"; unsigned long nan[2]={0xffffffff, 0x7fffffff}; z = *( double* )nan; cout << "z = " << z << "\n"; cout << HexDump(z) << " _fpclass(z) = " << FPClass(z) << "\n"; return 0; }
To learn more
For a brief explanation of numerical limits and how floating point numbers are laid out in memory, seeAnatomy of a floating point number.
For much more detail regarding exceptions and IEEE arithmetic in general, see
What every computer scientist should know about floating-point arithmetic.
From John D. Cook's blog
相关文章推荐
- Catching unhandled exceptions in SharePoint--Janne Mattila
- c#调用c++ dll. unable to find an entry point named function in dll
- IEEE floating-point exceptions in C
- IEEE floating-point exceptions in C
- c#调用c++ dll. unable to find an entry point named function in dll
- how do exceptions work (behind the scenes) in c++
- Enabling C++ exceptions and RTTI in kernel-mode code
- C++编译错误no type named ‘iterator_category’ in ‘class Point’,distance重名
- Remote Debugging GAC'd Assemblies in SharePoint
- Difference between getmessage and getlocalizedmessage in exceptions Java?
- 使用npm+babel+webpack+React搭建SharePoint hosted add-in开发环境
- 【C++】【LeetCode】24. Swap Nodes in Pairs
- 引入AOP 报错 error at ::0 formal unbound in pointcut
- The mixed programming in terms of matlab and C++
- error: in C++98 ‘vi’ must be initialized by constructor, not by ‘{...}’
- Temporary Variable in C++
- Name Mangling in C++
- usage of fscanf and other read functions in C/C++
- [Python] Handle Exceptions to prevent crashes in Python
- Question 35: Protected, or private, inheritance, as opposed to public inheritance, models which type of relationship in C++?