In safety programming, it is important to get alerted about the generation of floating-point Inf and NaN values in the process of computation, especially when those values only show up in the middle and get lost in the output due to operations such as casting to other data types that have no representation for Inf and NaN values, such as integers.
If we have sufficient understanding of the computation process, we can add checks to detect the generation of Inf and NaN values only when it is necessary. In the floating point computation, because NaN values will propagate through the computation and Inf values will propagate or become NaN through the computation, we can have the following first principles:
If the computation is expected to be completely in the floating-point domain, we can skip the checks for Inf and NaN values until the operation that can possibly lead to the loss of those values or the final output.
If the computation involves casting to other that have no representation for Inf and NaN values, we may need to add checks to detect the generation of Inf and NaN values in the middle of the computation.
But even if casting the floating point values to other data types that have no representation for Inf and NaN values can lead to the loss of those values and we shall check the values before the casting, sometimes we can eliminate the necessity of such checks if we know all the computations preceding the casting operation will not generate Inf and NaN values. So it’s important to understand the sources of Inf and NaN values in the floating-point computation.
In this post, we will show some sources of Inf and NaN values in the floating-point computation.
Floating Point Inf NaN Sources
The following code can be used for experimenting some sources of Inf and NaN values in the floating-point computation.
$ g++ inf_nan.cpp -o inf_nan $ ./inf_nan NAN is nan. NAN * 0.0f is nan. std::numeric_limits<float>::quiet_NaN() is nan. std::numeric_limits<float>::signaling_NaN() is nan. 1.0f / 0.0f is inf. -1.0f / 0.0f is inf. 0.0f / 0.0f is nan. -std::numeric_limits<float>::infinity() is inf. std::sqrt(-1.0f) is nan. std::log(-1.0f) is nan. std::numeric_limits<float>::max() is not inf or nan. Got 3.40282e+38. std::numeric_limits<float>::max() * 2.0f is inf. static_cast<float>(std::numeric_limits<int32_t>::max()) * static_cast<float>(std::numeric_limits<int32_t>::max()) is not inf or nan. Got 4.61169e+18. std::numeric_limits<float>::infinity() - std::numeric_limits<float>::infinity() is nan. std::numeric_limits<float>::infinity() + std::numeric_limits<float>::infinity() is inf. std::numeric_limits<float>::infinity() * 0.0f is nan. std::numeric_limits<float>::infinity() / 0.0f is inf. std::numeric_limits<float>::infinity() * 2.0f is inf. std::numeric_limits<float>::infinity() / 2.0f is inf.
The results of the code can be summarized as follows.
Some of the results are actually a little bit surprising to me. In my impression, for example, I thought 1.0f / 0.0f and 0.0f / 0.0f would behave the same way and both are nan, but 1.0f / 0.0f is actually inf.