Floating Point Inf NaN Sources

Introduction

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.

inf_nan.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
#include <cmath>
#include <iostream>
#include <limits>
#include <string>

void check_inf_nan_value(float value, const std::string& operation)
{
if (std::isnan(value))
{
std::cout << operation << " is nan." << std::endl;
}
else if (std::isinf(value))
{
std::cout << operation << " is inf." << std::endl;
}
else
{
std::cout << operation << " is not inf or nan. Got " << value << "."
<< std::endl;
}
}

int main()
{
check_inf_nan_value(NAN, "NAN");
check_inf_nan_value(NAN * 0.0f, "NAN * 0.0f");
check_inf_nan_value(std::numeric_limits<float>::quiet_NaN(),
"std::numeric_limits<float>::quiet_NaN()");
check_inf_nan_value(std::numeric_limits<float>::signaling_NaN(),
"std::numeric_limits<float>::signaling_NaN()");
check_inf_nan_value(1.0f / 0.0f, "1.0f / 0.0f");
check_inf_nan_value(-1.0f / 0.0f, "-1.0f / 0.0f");
check_inf_nan_value(0.0f / 0.0f, "0.0f / 0.0f");
check_inf_nan_value(-std::numeric_limits<float>::infinity(),
"-std::numeric_limits<float>::infinity()");
check_inf_nan_value(std::sqrt(-1.0f), "std::sqrt(-1.0f)");
check_inf_nan_value(std::log(-1.0f), "std::log(-1.0f)");
check_inf_nan_value(std::numeric_limits<float>::max(),
"std::numeric_limits<float>::max()");
check_inf_nan_value(std::numeric_limits<float>::max() * 2.0f,
"std::numeric_limits<float>::max() * 2.0f");
check_inf_nan_value(
static_cast<float>(std::numeric_limits<int32_t>::max()) *
static_cast<float>(std::numeric_limits<int32_t>::max()),
"static_cast<float>(std::numeric_limits<int32_t>::max()) * "
"static_cast<float>(std::numeric_limits<int32_t>::max())");
check_inf_nan_value(std::numeric_limits<float>::infinity() -
std::numeric_limits<float>::infinity(),
"std::numeric_limits<float>::infinity() - "
"std::numeric_limits<float>::infinity()");
check_inf_nan_value(std::numeric_limits<float>::infinity() +
std::numeric_limits<float>::infinity(),
"std::numeric_limits<float>::infinity() + "
"std::numeric_limits<float>::infinity()");
check_inf_nan_value(std::numeric_limits<float>::infinity() * 0.0f,
"std::numeric_limits<float>::infinity() * 0.0f");
check_inf_nan_value(std::numeric_limits<float>::infinity() / 0.0f,
"std::numeric_limits<float>::infinity() / 0.0f");
check_inf_nan_value(std::numeric_limits<float>::infinity() * 2.0f,
"std::numeric_limits<float>::infinity() * 2.0f");
check_inf_nan_value(std::numeric_limits<float>::infinity() / 2.0f,
"std::numeric_limits<float>::infinity() / 2.0f");
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
$ 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.

Operation Result
NAN nan
NAN * 0.0f nan
std::numeric_limits<float>::quiet_NaN() nan
std::numeric_limits<float>::signaling_NaN() nan
1.0f / 0.0f inf
-1.0f / 0.0f inf
0.0f / 0.0f nan
-std::numeric_limits<float>::infinity() inf
std::sqrt(-1.0f) nan
std::log(-1.0f) nan
std::numeric_limits<float>::max() 3.40282e+38
std::numeric_limits<float>::max() * 2.0f inf
static_cast<float>(std::numeric_limits<int32_t>::max()) * static_cast<float>(std::numeric_limits<int32_t>::max()) 4.61169e+18
std::numeric_limits<float>::infinity() - std::numeric_limits<float>::infinity() nan
std::numeric_limits<float>::infinity() + std::numeric_limits<float>::infinity() inf
std::numeric_limits<float>::infinity() * 0.0f nan
std::numeric_limits<float>::infinity() / 0.0f inf
std::numeric_limits<float>::infinity() * 2.0f inf
std::numeric_limits<float>::infinity() / 2.0f inf

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.

Author

Lei Mao

Posted on

07-14-2024

Updated on

07-14-2024

Licensed under


Comments