3.6 NULL, NaN, and Inf

Having learned the special missing value representation NA in Section 2.15, we will introduce three additional special values, namely the NULL, NaN, and Inf. During the process, we will talk about their relationships to NA as well. All NULL, NaN, and Inf are reserved words in R.

Before introducing these three special values, let’s review the structure, internal storage type, and length of NA. It is worth recalling that NA is a length-1 logical constant that indicates a missing value. Keep in mind that NA is different from "NA" (a character vector).

str(NA)
#>  logi NA
typeof(NA)
#> [1] "logical"
length(NA)
#> [1] 1

3.6.1 NULL

First, let’s take a look at str(), typeof() and length() of NULL.

str(NULL)
#>  NULL
typeof(NULL)
#> [1] "NULL"
length(NULL)
#> [1] 0

From the output, you’ll see that NULL is a length-0 object often returned by expressions and functions whose value is undefined.

a. Undefined field of a list

The first scenario of NULL is when you try to access an element of a list that is undefined.

my_list <- list(num = 1:3, char = c("a", "b"))
my_list$logi
#> NULL

Here, the result is NULL since logi is not a defined field in my_list.

b. Remove an element from a list

You can remove an element from a list by assign it the NULL value.

length(my_list)
#> [1] 2
my_list$num <- NULL
length(my_list)
#> [1] 1
my_list
#> $char
#> [1] "a" "b"

As you can see from the output, the element num is removed from my_list, leading to the length of my_list being reduced by 1.

c. Initialize a list of certain length

The NULL value is useful to serve as the default initial value when you want to create a list of certain length using the vector() function.

my_list <- vector(mode = "list", length = 3)
my_list
#> [[1]]
#> NULL
#> 
#> [[2]]
#> NULL
#> 
#> [[3]]
#> NULL

It is worth mentioning that the vector() function is also useful to initialize a vector of given mode and length.

vector("numeric", length = 2)  ##default is 0
#> [1] 0 0
vector("logical", length = 2)  ##default is FALSE
#> [1] FALSE FALSE
vector("integer", length = 2)  ##default is 0
#> [1] 0 0
vector("character", length = 2)  ##default is empty string
#> [1] "" ""

To check if an element is NULL, you can’t use the logical comparison == NULL. Instead, you need to use the is.null() function.

a <- NULL
a == NULL
#> logical(0)
is.null(a)
#> [1] TRUE

It is worth explaining the result of a == NULL is logical(0), representing a logical vector of length 0. The underlying reason is that NULL contains no value and is of length 0. As the == comparison returns a logical type object, which in turn leads to a logical vector of length 0.

d. NULL values when creating a vector

If you create a vector with NULL values, all NULL values will be removed if there exists at least one defined values. If all of them are NULL values, only one of them will be kept, but that vector’s length will be 0. Note that NULL is fundamentally different from NA. NA indicates that there is an underlying value, although the actual value is not available to us.

c(NULL, NULL, 1, NULL)
#> [1] 1
c(NULL, NULL)
#> NULL
c(NA, NA)
#> [1] NA NA

One final point on NULL is that you can technically perform calculations on NULL. However, from the example below, you’ll realize operations on NULL will only change the class, leaving the length unchanged.

d <- NULL
d <- d + 5
str(d)  # the class is changed to numeric, but the length is still 0
#>  num(0)

3.6.2 NaN

NaN, represents Not a Number, usually appears when you divide 0 by 0, indicating the result is not well-defined.

0/0
#> [1] NaN

Again, it is worth to look at str(), typeof() and length() of NaN.

str(NaN)
#>  num NaN
typeof(NaN)
#> [1] "double"
length(NaN)
#> [1] 1

As you can see from the results, NaN is a numeric vector of length 1, with the value NaN.

To check if a value is NaN, you cannot use the == NaN similar to checking missing values. Instead, you need to use the function is.nan().

a <- NaN
a == NaN  ##resulting an NA value
#> [1] NA
is.nan(a)  ##the correct way to check if the value is NaN
#> [1] TRUE
is.nan(c(NA, 1, NaN))
#> [1] FALSE FALSE  TRUE

3.6.3 Inf

The last special symbol we want to introduce in this section is Inf, which represents positive infinity (\(\infty\)), corresponding to a proper mathematical limit. Similarly, we have negative infinity, denoted as -Inf.

1/0
#> [1] Inf
-2/0
#> [1] -Inf
Inf > 3
#> [1] TRUE
Inf < -1
#> [1] FALSE
Inf + Inf
#> [1] Inf
-Inf + 1e+10
#> [1] -Inf
1/0 - 1/0  #it equals 0/0, hencing NaN
#> [1] NaN

Again, it is worth to look at str(), typeof() and length() of Inf.

str(Inf)
#>  num Inf
typeof(Inf)
#> [1] "double"
length(Inf)
#> [1] 1

As you can see from the results, similar to NaN, Inf is a numeric vector of length 1, but with the value Inf.

To check whether a value is finite or infinite, you can use the is.finite() and is.infinite() function.

is.finite(1/0)
#> [1] FALSE
is.infinite(-3/0)
#> [1] TRUE

3.6.4 A comparison of the four special values in R

We would like to summarize the different behaviors of the four special values in R in the following table.

Summary NA NULL NaN Inf
class() "logical" "NULL" "numeric" "numeric"
length() 1 0 1 1
check is.na() is.null() is.nan() is.finite()

3.6.5 Exercises

Suppose x <- c(NA, NULL, Inf, NaN), answer the following questions. (First, try to solve without using R. Then, confirm with R code)

  1. What’s the length of x?
  2. What’s the class and the storage type of x?
  3. What’s the value of x + 1? Explain the reason for each element in the result.
  4. What’s the value of x == x? Explain the reason for each element in the result.