[r] Check if the number is integer

I was surprised to learn that R doesn't come with a handy function to check if the number is integer.

is.integer(66) # FALSE

The help files warns:

is.integer(x) does not test if x contains integer numbers! For that, use round, as in the function is.wholenumber(x) in the examples.

The example has this custom function as a "workaround"

is.wholenumber <- function(x, tol = .Machine$double.eps^0.5)  abs(x - round(x)) < tol
is.wholenumber(1) # is TRUE

If I would have to write a function to check for integers, assuming I hadn't read the above comments, I would write a function that would go something along the lines of

check.integer <- function(x) {
    x == round(x)
}

Where would my approach fail? What would be your work around if you were in my hypothetical shoes?

This question is related to r integer rounding

The answer is


you can use simple if condition like:

if(round(var) != var)­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­

If you prefer not to write your own function, try check.integer from package installr. Currently it uses VitoshKa's answer.

Also try check.numeric(v, only.integer=TRUE) from package varhandle, which has the benefit of being vectorized.


[UPDATE] ==============================================================

Respect to the [OLD] answer here below, I have discovered that it worked because I have put all the numbers in a single atomic vector; one of them was a character, so every one become characters.

If we use a list (hence, coercion does not happen) all the test pass correctly but one: 1/(1 - 0.98), which remains a numeric. This because the tol parameter is by default 100 * .Machine$double.eps and that number is far from 50 little less than the double of that. So, basically, for this kind of numbers, we have to decide our tolerance!

So if you want all test became TRUE, you can assertive::is_whole_number(x, tol = 200 * .Machine$double.eps)

Anyway, I confirm that IMO assertive remains the best solution.

Here below a reprex for this [UPDATE].

expect_trues_c <- c(
  cl = sqrt(2)^2,
  pp = 9.0,
  t = 1 / (1 - 0.98),
  ar0 = 66L,
  ar1 = 66,
  ar2 = 1 + 2^-50,
  v = 222e3,
  w1 = 1e4,
  w2 = 1e5,
  v2 = "1000000000000000000000000000000000001",
  an = 2 / 49 * 49,
  ju1 = 1e22,
  ju2 = 1e24,
  al = floor(1),
  v5 = 1.0000000000000001 # this is under machine precision!
)

str(expect_trues_c)
#>  Named chr [1:15] "2" "9" "50" "66" "66" "1" "222000" "10000" "1e+05" ...
#>  - attr(*, "names")= chr [1:15] "cl" "pp" "t" "ar0" ...
assertive::is_whole_number(expect_trues_c)
#> Warning: Coercing expect_trues_c to class 'numeric'.
#>                      2                      9                     50 
#>                   TRUE                   TRUE                   TRUE 
#>                     66                     66                      1 
#>                   TRUE                   TRUE                   TRUE 
#>                 222000                  10000                 100000 
#>                   TRUE                   TRUE                   TRUE 
#>                  1e+36                      2                  1e+22 
#>                   TRUE                   TRUE                   TRUE 
#> 9.9999999999999998e+23                      1                      1 
#>                   TRUE                   TRUE                   TRUE



expect_trues_l <- list(
  cl = sqrt(2)^2,
  pp = 9.0,
  t = 1 / (1 - 0.98),
  ar0 = 66L,
  ar1 = 66,
  ar2 = 1 + 2^-50,
  v = 222e3,
  w1 = 1e4,
  w2 = 1e5,
  v2 = "1000000000000000000000000000000000001",
  an = 2 / 49 * 49,
  ju1 = 1e22,
  ju2 = 1e24,
  al = floor(1),
  v5 = 1.0000000000000001 # this is under machine precision!
)

str(expect_trues_l)
#> List of 15
#>  $ cl : num 2
#>  $ pp : num 9
#>  $ t  : num 50
#>  $ ar0: int 66
#>  $ ar1: num 66
#>  $ ar2: num 1
#>  $ v  : num 222000
#>  $ w1 : num 10000
#>  $ w2 : num 1e+05
#>  $ v2 : chr "1000000000000000000000000000000000001"
#>  $ an : num 2
#>  $ ju1: num 1e+22
#>  $ ju2: num 1e+24
#>  $ al : num 1
#>  $ v5 : num 1
assertive::is_whole_number(expect_trues_l)
#> Warning: Coercing expect_trues_l to class 'numeric'.
#> There was 1 failure:
#>   Position              Value      Cause
#> 1        3 49.999999999999957 fractional
assertive::is_whole_number(expect_trues_l, tol = 200 * .Machine$double.eps)
#> Warning: Coercing expect_trues_l to class 'numeric'.
#>     2.0000000000000004                      9     49.999999999999957 
#>                   TRUE                   TRUE                   TRUE 
#>                     66                     66     1.0000000000000009 
#>                   TRUE                   TRUE                   TRUE 
#>                 222000                  10000                 100000 
#>                   TRUE                   TRUE                   TRUE 
#>                  1e+36     1.9999999999999998                  1e+22 
#>                   TRUE                   TRUE                   TRUE 
#> 9.9999999999999998e+23                      1                      1 
#>                   TRUE                   TRUE                   TRUE



expect_falses <- list(
  bb = 5 - 1e-8,
  pt1 = 1.0000001,
  pt2 = 1.00000001,
  v3 = 3243.34,
  v4 = "sdfds"
)

str(expect_falses)
#> List of 5
#>  $ bb : num 5
#>  $ pt1: num 1
#>  $ pt2: num 1
#>  $ v3 : num 3243
#>  $ v4 : chr "sdfds"
assertive::is_whole_number(expect_falses)
#> Warning: Coercing expect_falses to class 'numeric'.
#> Warning in as.this_class(x): NAs introduced by coercion
#> There were 5 failures:
#>   Position              Value      Cause
#> 1        1 4.9999999900000001 fractional
#> 2        2 1.0000001000000001 fractional
#> 3        3 1.0000000099999999 fractional
#> 4        4 3243.3400000000001 fractional
#> 5        5               <NA>    missing
assertive::is_whole_number(expect_falses, tol = 200 * .Machine$double.eps)
#> Warning: Coercing expect_falses to class 'numeric'.

#> Warning: NAs introduced by coercion
#> There were 5 failures:
#>   Position              Value      Cause
#> 1        1 4.9999999900000001 fractional
#> 2        2 1.0000001000000001 fractional
#> 3        3 1.0000000099999999 fractional
#> 4        4 3243.3400000000001 fractional
#> 5        5               <NA>    missing

Created on 2019-07-23 by the reprex package (v0.3.0)

[OLD] =================================================================

IMO the best solution comes from the assertive package (which, for the moment, solve all positive and negative examples in this thread):

are_all_whole_numbers <- function(x) {
  all(assertive::is_whole_number(x), na.rm = TRUE)
}

are_all_whole_numbers(c(
  cl = sqrt(2)^2,
  pp = 9.0,
  t = 1 / (1 - 0.98),
  ar0 = 66L,
  ar1 = 66,
  ar2 = 1 + 2^-50,
  v = 222e3,
  w1 = 1e4,
  w2 = 1e5,
  v2 = "1000000000000000000000000000000000001",
  an = 2 / 49 * 49,
  ju1 = 1e22,
  ju2 = 1e24,
  al = floor(1),
  v5 = 1.0000000000000001 # difference is under machine precision!
))
#> Warning: Coercing x to class 'numeric'.
#> [1] TRUE

are_all_not_whole_numbers <- function(x) {
  all(!assertive::is_whole_number(x), na.rm = TRUE)
}

are_all_not_whole_numbers(c(
  bb = 5 - 1e-8,
  pt1 = 1.0000001,
  pt2 = 1.00000001,
  v3 = 3243.34,
  v4 = "sdfds"
))
#> Warning: Coercing x to class 'numeric'.
#> Warning in as.this_class(x): NAs introduced by coercion
#> [1] TRUE

Created on 2019-07-23 by the reprex package (v0.3.0)


It appears that you do not see the need to incorporate some error tolerance. It would not be needed if all integers came entered as integers, however sometimes they come as a result of arithmetic operations that loose some precision. For example:

> 2/49*49
[1] 2
> check.integer(2/49*49)
[1] FALSE 
> is.wholenumber(2/49*49)
[1] TRUE

Note that this is not R's weakness, all computer software have some limits of precision.


Here is one, apparently reliable way:

check.integer <- function(N){
    !grepl("[^[:digit:]]", format(N,  digits = 20, scientific = FALSE))
}

check.integer(3243)
#TRUE
check.integer(3243.34)
#FALSE
check.integer("sdfds")
#FALSE

This solution also allows for integers in scientific notation:

> check.integer(222e3)
[1] TRUE

I am not sure what you are trying to accomplish. But here are some thoughts:
1. Convert to integer:
num = as.integer(123.2342)
2. Check if a variable is an integer:
is.integer(num)
typeof(num)=="integer"


Reading the R language documentation, as.integer has more to do with how the number is stored than if it is practically equivalent to an integer. is.integer tests if the number is declared as an integer. You can declare an integer by putting a L after it.

> is.integer(66L)
[1] TRUE
> is.integer(66)
[1] FALSE

Also functions like round will return a declared integer, which is what you are doing with x==round(x). The problem with this approach is what you consider to be practically an integer. The example uses less precision for testing equivalence.

> is.wholenumber(1+2^-50)
[1] TRUE
> check.integer(1+2^-50)
[1] FALSE

So depending on your application you could get into trouble that way.


From Hmisc::spss.get:

all(floor(x) == x, na.rm = TRUE)

much safer option, IMHO, since it "bypasses" the machine precision issue. If you try is.integer(floor(1)), you'll get FALSE. BTW, your integer will not be saved as integer if it's bigger than .Machine$integer.max value, which is, by default 2147483647, so either change the integer.max value, or do the alternative checks...


Here's a solution using simpler functions and no hacks:

all.equal(a, as.integer(a))

What's more, you can test a whole vector at once, if you wish. Here's a function:

testInteger <- function(x){
  test <- all.equal(x, as.integer(x), check.attributes = FALSE)
  if(test == TRUE){ return(TRUE) }
  else { return(FALSE) }
}

You can change it to use *apply in the case of vectors, matrices, etc.


For a vector m, m[round(m) != m] will return the indices of values in the vector that are not integers.


In R, whether a number is numeric or integer can be determined by class function. Generally all numbers are stored as numeric and to explicitly define a number as integer we need to specify 'L' after the number.

Example:

x <- 1

class(x)

[1] "numeric"

x <- 1L

class(x)

[1] "integer"

I hope this is what was needed. Thanks :)


Once can also use dplyr::near:

library(dplyr)

near(a, as.integer(a))

It applies to any vector a, and has an optional tolerance parameter.


Examples related to r

How to get AIC from Conway–Maxwell-Poisson regression via COM-poisson package in R? R : how to simply repeat a command? session not created: This version of ChromeDriver only supports Chrome version 74 error with ChromeDriver Chrome using Selenium How to show code but hide output in RMarkdown? remove kernel on jupyter notebook Function to calculate R2 (R-squared) in R Center Plot title in ggplot2 R ggplot2: stat_count() must not be used with a y aesthetic error in Bar graph R multiple conditions in if statement What does "The following object is masked from 'package:xxx'" mean?

Examples related to integer

Python: create dictionary using dict() with integer keys? How to convert datetime to integer in python Can someone explain how to append an element to an array in C programming? How to get the Power of some Integer in Swift language? python "TypeError: 'numpy.float64' object cannot be interpreted as an integer" What's the difference between integer class and numeric class in R PostgreSQL: ERROR: operator does not exist: integer = character varying C++ - how to find the length of an integer Converting binary to decimal integer output Convert floats to ints in Pandas?

Examples related to rounding

How to round a numpy array? How to pad a string with leading zeros in Python 3 Python - round up to the nearest ten How to round a Double to the nearest Int in swift? Using Math.round to round to one decimal place? How to round to 2 decimals with Python? Rounding to 2 decimal places in SQL Rounding to two decimal places in Python 2.7? Round a floating-point number down to the nearest integer? Rounding BigDecimal to *always* have two decimal places