Contents
When programming, there are certain error condition that might arise from your code. If the compiler catches any compilation errors, it will stop compilation and tell you what and where the error is. The compiler also may issue a warning for certain constructs, which may or may not give you problems during runtime.
Here are some floating point conditions that you as a programmer should be aware of when doing floating point arithmetic (any of addition, subtraction, multiplication, and division), namely INF
, IND
, and NaN
(QNAN and SNAN) conditions and errors.
Positive | Negative | Meaning |
1.#INF | -1.#INF | Infinity |
1.#SNAN | -1.#SNAN | Signalling NaN |
1.#QNAN | -1.#QNAN | Quiet NaN |
1.#IND | -1.#IND | Indefinite / Indeterminite NaN |
The floating point specification is called IEEE 754-2008, and is behind a paywall. However, the Wikipedia page is quite detailed.
Read also: How to check for NAN / INF / IND in C++
C++ NaN
stands for not a number. It is a value that depicts an undefined or unspecified value with floating point. The concept of NaN was introduced in 1985 by the IEEE 754 floating-point standard in which the concept of infinities was also given.
Here is an example of what NaN values can cause in a real video game: black dots.
There are two kinds of NaNs
, quiet and signalling. Both are very similar, and in most instances they are identical. With MSVC, the signalling NaNs (sNaN) are by default quiet NaNs. They won’t raise any hardware exceptions (as all floating point exceptions are) and you won’t notice anything wrong until the printouts contain nothing but 1.#QNAN
and the simulation fails miserably.
C++ INF
denotes “infinity”. Due to the finite nature of floating point numbers (32-bit
for floats, 64-bit
for doubles), infinite is represented by a finite value.
This type of error condition / value arises when the resultant number is overflowing or underflowing the capacity of the floating point number. In other words, the value is too large or too small to be represented as a floating point value.
So the value appears as #INF
.
The easiest way to use get an infinite number is asking for it.
#include <limits> // Infinite auto positive_inf = std::numeric_limits<double>::infinity(); auto negative_inf = positive_inf * -1; std::cout << "Positive infinity: " << positive_inf << std::endl; std::cout << "Negative infinity: " << negative_inf << std::endl; // Output // Positive infinity: 1.#INF // Negative infinity: -1.#INF // Or division by zero double zero = 0.0; double divbyzero = 1.0 / zero; // Output // Division by zero: 1.#INF
Any operation with infinity gives another infinite value, so it behaves like a plague, and it’ll kill simulations where it can propagate to complete data sets within iterations.
std::cout << "Add: " << add << " Sub: " << sub << " Mul: " << mul << " Div: " << div << std::endl; // Output: // Add: 1.#INF Sub: 1.#INF Mul: 1.#INF Div: 1.#INF
When doing stream formatting, the infinity number might be mangled and may show something not expected.
#include <iomanip> // Formatting (scientific) for(size_t i=0; i<10; i++) { std::cout << "Precision: " << std::scientific << i << std::setprecision(i) << " inf: " << positive_inf << std::endl; } // Output // Precision: 0 inf: 1.#INF00e+000 // Precision: 1 inf: 1.$e+000 // Precision: 2 inf: 1.#Je+000 // Precision: 3 inf: 1.#IOe+000 // Precision: 4 inf: 1.#INFe+000 // Precision: 5 inf: 1.#INF0e+000 // Precision: 6 inf: 1.#INF00e+000 // Precision: 7 inf: 1.#INF000e+000 // Precision: 8 inf: 1.#INF0000e+000 // Precision: 9 inf: 1.#INF00000e+000 // Formatting (fixed) for (size_t i=0; i<10; i++) { std::cout << "Precision: " << std::fixed << i << std::setprecision(i) << " inf: " << positive_inf << std::endl; } // Output // Precision: 0 inf: 1 // Precision: 1 inf: 1.$ // Precision: 2 inf: 1.#J // Precision: 3 inf: 1.#IO // Precision: 4 inf: 1.#INF // Precision: 5 inf: 1.#INF0 // Precision: 6 inf: 1.#INF00 // Precision: 7 inf: 1.#INF000 // Precision: 8 inf: 1.#INF0000 // Precision: 9 inf: 1.#INF00000
Fixed notation with precision 0 is very dangerous! There is no indication this is an infinite number!
C++ #IND
is a special kind of NaN
, when the result can’t be determined. This is particular true for mathematical methods where certain inputs are undefined (acos
, sqrt
, and friends).
For instance, zero divided by zero (0.0 / 0.0
) is undefined in the field of mathematics or #IND
. It’s the same with floating points, it produced an indefinite and indeterminate number.
double a = 0.0; double b = 0.0 / a; double negative_sqrt = sqrt(-1); std::cout << "0.0/0.0 = " << b << std::endl; std::cout << "sqrt(-1) = " << negative_sqrt << std::endl; // Output // 0.0/0.0 = -1.#IND // sqrt(-1) = -1.#IND
There are a couple of ways to trap NANs, and some requires a debugger attached to get the best results.
When comparing the variable against itself, it will produce opposite results than with normal numbers.
double inf = std::numeric_limits<double>::infinity(); double nan = std::numeric_limits<double>::quiet_NaN(); double ind = sqrt(-1); if (nan != nan) std::cout << "nan != nan" << std::endl; if (ind != ind) std::cout << "ind != ind" << std::endl; if (inf != inf) std::cout << "inf != inf" << std::endl; // Output // nan != nan // ind != ind // std::isnan(nan) is true // std::isnan(ind) is true
Note, infinity
is a number!
C++11
introduced a couple of methods to determine if a value is a NaN
or not, which are residing the the std::
namespace.
// Method 2, std::isnan if (std::isnan(nan)) std::cout << "std::isnan(nan) is true" << std::endl; if (std::isnan(ind)) std::cout << "std::isnan(ind) is true" << std::endl; if (std::isnan(inf)) std::cout << "std::isnan(inf) is true" << std::endl; // Output // std::isnan(nan) is true // std::isnan(ind) is true
Note that inf
is still missing, which is correct behavior.
The use of _controlfp
and _control87
family will alter the floating-point control word, essentially making the computer issue a hardware exception when a floating point exception occurs. The default with MSVC is to silence floating point exceptions, with the possibility to silently destroy simulations.
I personally recommend turning on all floating point exceptions, so all (or most) cases can be reviewed and the code made more robust.
The usage is a bit counter-intuitive, but after a while you’ll get used to it. Here is what works for me.
// According to the docs, always clear fp control word auto state = _clearfp(); state = _control87(0,0);
This will reset, and get current floating point control word state.
With this, we can modify the floating point control word to our liking.
// This will turn ON FPE for zerodiv state = _control87(state & ~_EM_ZERODIVIDE, _MCW_EM); // This WILL NOT fail const double sqrtneg = sqrt(-1); // This WILL fail due to zerodiv const double zero = 0.0; const double zerodivresult = 1.0 / zero;
Otherwise, we can break on indeterminate (#IND
) with _EM_INVALID
.
// This will turn on FPE for #IND state = _control87(state & ~_EM_INVALID, _MCW_EM); // This WILL fail const double sqrtneg = sqrt(-1); // This WILL NOT fail and it'll produce #inf const double zero = 0.0; const double zerodivresult = 1.0 / zero;
If you want to turn on two or more FPEs, you must do some bit arithmetic to set the right switches.
// This will turn on FPE for #IND and zerodiv state = _control87(state & ~(_EM_ZERODIVIDE|_EM_INVALID), _MCW_EM);
If you want to turn all FPEs, this will do it.
// All FPEs state = _control87(state & ~(_EM_INEXACT|_EM_UNDERFLOW |_EM_OVERFLOW|_EM_ZERODIVIDE| _EM_INVALID|_EM_DENORMAL), _MCW_EM);
To find out what floating point exception which are active, this snippet will help.
bool inexact = 0 == (state & _EM_INEXACT); bool underflow = 0 == (state & _EM_UNDERFLOW); bool overflow = 0 == (state & _EM_OVERFLOW); bool zerodiv = 0 == (state & _EM_ZERODIVIDE); bool invalid = 0 == (state & _EM_INVALID); bool denorm = 0 == (state & _EM_DENORMAL); std::cout << std::boolalpha << "Will break on: n" << "Inexact: " << inexact << "n" << "Underflow: " << underflow << "n" << "Overflow: " << overflow << "n" << "Zerodiv: " << zerodiv << "n" << "Invalid: " << invalid << "n" << "Denormal: " << denorm << "n"; // Output // Will break on: // Inexact: false // Underflow: false // Overflow: false // Zerodiv: true // Invalid: true // Denormal: false
PS: Most of the printouts in this post is specific to Visual Studio. GCC will print out a simple nan
or inf
.
Professional Software Developer, doing mostly C++. Connect with Kent on Twitter.