Skip to contents

Test element-wise near-equality to the natural numbers while allowing for small numeric errors.

Usage

is_natural(
  x,
  strict = TRUE,
  allow_zero = FALSE,
  allow_NA = FALSE,
  tol = .Machine$double.eps^0.5
)

all_natural(
  x,
  strict = TRUE,
  allow_zero = FALSE,
  allow_NA = FALSE,
  tol = .Machine$double.eps^0.5
)

make_natural(
  x,
  strict = TRUE,
  allow_zero = FALSE,
  allow_NA = FALSE,
  all = TRUE,
  tol = .Machine$double.eps^0.5
)

Arguments

x

object to test.

strict

Exclude zero from the natural numbers?

allow_zero

TRUE or FALSE: allow zero-length x of the correct type?

allow_NA

TRUE or FALSE: allow NAs of the correct type in x?

tol

A small positive number. Numbers that differ less in value than tol are considered equal.

all

TRUE or FALSE: use all_natural() instead of is_natural()?

Value

is_natural() and all_natural(): TRUE or FALSE indicating if x is a vector of the appropriate length with only natural numbers. make_natural(): x, rounded and coerced to integer.

Details

Natural numbers are the positive integers (1, 2, 3, etc.). Zero is considered a natural number if argument strict is FALSE. integer(0) and numeric(0) are considered natural numbers if argument allow_zero is TRUE. Numbers that are too large to be represented as integers, Inf, NaN, and NULL are never considered natural numbers in this implementation.

is_natural() and all_natural() allow for small numeric errors when comparing numbers. Such numeric errors can arise because of rounding or representation error. As the Note at == warns, x == round(x) does not allow for such errors but tests exact equality. Functions from other packages with names like integerish frequently do not allow for small numeric errors but are instead intended to allow values that are stored as doubles (e.g., 3) in addition to integer-type values (e.g., 3L).

If allow_NA is TRUE, is_natural() and all_natural() return TRUE for NA_integer_ and NA_real_ but not for the other NAs or NaN.

Notes

make_natural(x, all = FALSE) and make_natural(x, all = TRUE) throw an error if x is not natural according to is_natural(x) or all_natural(x), respectively.

The code of is_natural() and all_natural() is partly based on the example is.wholenumber() in is.integer().

Programming notes

Use of is_natural(x) or all_natural(x) should be followed by assigning the rounded value to the argument: x <- as.integer(round(x)). Alternatively, assign the result of make_natural(x) to x without using is_natural(x) or all_natural(x) inside stopifnot.

is.integer() does not check that x is a natural number (nor if x is a whole number) but rather that x is of type integer (see the Note in is.integer()).

See also

progutils::are_equal() to check for element-wise near-equality of numbers; all.equal() to check more generally for near-equality; identical() to check for exact equality; Comparison to compare two vectors using binary operators; match() and progutils::not_in() to compare character vectors; R FAQ 7.31 for background on numerical equality.

The vignettes about design choices and about type coercion.

Other collections of checks on type and length: all_characters(), all_names(), is_logical(), is_number(), is_zerolength()

Examples

is_natural(x = 5 + 1e-10) # TRUE
#> [1] TRUE
# Zero is not considered a natural number if 'strict' is TRUE:
is_natural(x = 1e-10, strict = TRUE) # FALSE
#> [1] FALSE
try(make_natural(x = 1e-10, strict = TRUE)) # Error
#> Error in make_natural(x = 1e-10, strict = TRUE) : 
#>   checkinput::all_natural(1e-10) is not TRUE
is_natural(x = 1e-10, strict = FALSE) # TRUE
#> [1] TRUE
make_natural(x = 1e-10, strict = FALSE) # 0
#> [1] 0
is_natural(x = -1e-10, strict = FALSE) # FALSE: wrong sign
#> [1] FALSE
is_natural(x = Inf, strict = FALSE) # FALSE
#> [1] FALSE
is_natural(x = "a") # FALSE
#> [1] FALSE
is_natural(x = 1:2) # FALSE: wrong length
#> [1] FALSE

# Allowing for small numeric errors is important
x <- sqrt(2)^2
is_natural(x = x) # TRUE
#> [1] TRUE
x == 2 # FALSE!
#> [1] FALSE
x - 2 # about 4.44e-16
#> [1] 4.440892e-16

all_natural(x = c(3, 5 + 1e-10)) # TRUE
#> [1] TRUE
try(make_natural(x = c(3, 5 + 1e-10))) # c(3L, 5L)
#> [1] 3 5
# Zero is not considered a natural number if 'strict' is TRUE:
all_natural(x = c(1e-10, 3, 5), strict = TRUE) # FALSE
#> [1] FALSE
all_natural(x = c(1e-10, 3, 5), strict = FALSE) # TRUE
#> [1] TRUE
all_natural(x = c(-1e-10, 3, 5), strict = FALSE) # FALSE: wrong sign
#> [1] FALSE
all_natural(x = c(3, 5, Inf), strict = FALSE) # FALSE
#> [1] FALSE
all_natural(x = "a") # FALSE
#> [1] FALSE
all_natural(x = 1:2) # TRUE
#> [1] TRUE

# Illustrate the need to follow use of is_natural(x) or all_natural(x) by
# assigning the rounded value to the argument
toy_fun_erroneous <- function(x) {
  stopifnot(is_natural(x))
  seq_len(x)
}

toy_fun_correct <- function(x) {
  stopifnot(is_natural(x))
  x <- round(x)
  seq_len(x)
}

toy_fun_safe <- function(x, all = TRUE) {
  x <- make_natural(x, all = all)
  seq_len(x)
}

toy_fun_erroneous(x = 5 - 1e-8) # 1:4
#> [1] 1 2 3 4
toy_fun_correct(x = 5 - 1e-8) # 1:5
#> [1] 1 2 3 4 5
toy_fun_safe(x = 5 - 1e-8) # 1:5
#> [1] 1 2 3 4 5

try(toy_fun_erroneous(x = 5.1)) # Error: is_natural(x) is not TRUE
#> Error in toy_fun_erroneous(x = 5.1) : is_natural(x) is not TRUE
try(toy_fun_correct(x = 5.1)) # Error: is_natural(x) is not TRUE
#> Error in toy_fun_correct(x = 5.1) : is_natural(x) is not TRUE
try(toy_fun_safe(x = 5.1, all = FALSE)) # Error: is_natural(x) is not TRUE
#> Error in make_natural(x, all = all) : 
#>   checkinput::is_natural(x) is not TRUE
try(toy_fun_safe(x = 5.1, all = TRUE)) # Error: all_natural(x) is not TRUE
#> Error in make_natural(x, all = all) : 
#>   checkinput::all_natural(x) is not TRUE