Check if the number is integer

149,014

Solution 1

Another alternative is to check the fractional part:

x%%1==0

or, if you want to check within a certain tolerance:

min(abs(c(x%%1, x%%1-1))) < tol

Solution 2

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.

Solution 3

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

Solution 4

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.

Solution 5

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.

Share:
149,014
Roman Luštrik
Author by

Roman Luštrik

I'm an analyst with roots in veterinary medicine, biology/ecology and biostatistics. I work with data from various fields of natural (genetics, ecology, biotechnology...) and social sciences (e.g. official statistics, economy). Having fun with cloud solutions like AWS. My tool of choice is R, but I can also somewhat handle Python, HTML, CSS. Ask me about reproducible research and version control. I feed many, many cats.

Updated on January 28, 2021

Comments

  • Roman Luštrik
    Roman Luštrik about 3 years

    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?

  • John
    John over 13 years
    just in case some people don't quite get what happened here... if you enter as.integer(2/49*49) you get 1 !! [BTW, it is ever so frustrating that R doesn't present the result of the initial calculation as 2.0 to represent that the value has some decimal component) see... stackoverflow.com/questions/1535021/…
  • Roman Luštrik
    Roman Luštrik over 13 years
    I'm just making sure the users enters an appropriate number - we're talking about the number of "subjects", which can be only an integer.
  • wch
    wch about 12 years
    This doesn't look very reliable to me: check.integer(1e4) is TRUE, while check.integer(1e5) is FALSE.
  • Joshua Ulrich
    Joshua Ulrich about 11 years
    -1 This is worse than is.wholenumber, or any of the other solutions provided in other answers. These shouldn't be different: check.integer(1e22); check.integer(1e23). You can obviously change the regex to fix this, but this approach is dreadful. (Comment comes from attribution in the installr package.)
  • Gavin Simpson
    Gavin Simpson about 11 years
    The last if else could be done with simply isTRUE(test). Indeed that is all you need to replace the if else clause and the return statements as R automatically returns the result of the last evaluation.
  • VitoshKa
    VitoshKa about 11 years
    @Joshua, Your comment is completely misleading for three reasons. First, 1e22 in your example cannot be represented accurately, and all non-regexp based solutions will fail. For example the now accepted solution (1e20+1.1)%%1 will give you 0 with a warning! Second, does this string represent an integer "1313213121313232321123213"? If you think it does, then my solution is the only one which works at all!
  • VitoshKa
    VitoshKa about 11 years
    And finally, how do you think R parser understands that you entered an integer if not by regexp-type matching? From what you say, R parser is "dreadful". If I would implement a complete specification of an integer in my reg-exp it will never fail!
  • Ben Bolker
    Ben Bolker over 10 years
    does the tolerance-checking suggestion really work?? x <- 5-1e-8; x%%1 gives 0.9999999 (which would imply if tol==1e-5 for example) that x is not an integer.
  • James
    James over 10 years
    @BenBolker Good catch, it works for positive perturbations I think. I've changed it to an alternative solution should work.
  • PatrickT
    PatrickT almost 9 years
    testInteger(1.0000001) [1] FALSE testInteger(1.00000001) [1] TRUE
  • PatrickT
    PatrickT almost 9 years
    check.Integer(1.000001) [1] FALSE check.Integer(1.0000001) [1] TRUE
  • Cath
    Cath almost 9 years
    @James, I think it should be min(abs(c(x%%1, x%%1-1))) < tol instead of abs(min(x%%1, x%%1-1)) < tol otherwise, you'll get FALSE for any integer...
  • PatrickT
    PatrickT almost 9 years
    It does here. I just checked again. I can't explain it. Here a screenshot: postimg.org/image/xrbx1dlen
  • VitoshKa
    VitoshKa almost 9 years
    @PatrickT, I see. It's the default digit's argument. use format(40, scientific = FALSE, digits = 20) instead. I have updated the answer. Thanks for spotting it.
  • PatrickT
    PatrickT almost 9 years
    @VitoshKa, much better, but there's a break at 15 digits, not 20 - is that expected? check.integer(1.000000000000001) [1] FALSE check.integer(1.0000000000000001) [1] TRUE
  • VitoshKa
    VitoshKa almost 9 years
    @PatrickT You are in the realm of machine dependent rounding errors. In that respect my solution is the same as the accepted one 1.0000000000000001 == 1L [1] TRUE. But my solution is better if you already get a number in string form check.integer("1000000000000000000000000000000000001") [1] TRUE
  • PatrickT
    PatrickT almost 9 years
    @VitoshKa, couldn't that problem be avoided if you convert the number to a character and look for the presence of non-zero digits after the decimal dot marker? I assumed that was the point of your choice of grepl?
  • VitoshKa
    VitoshKa almost 9 years
    @PatrickT. That's precisely what format does. It converts to character.
  • PatrickT
    PatrickT almost 9 years
    @VitoshKa, thanks! Ah got it: ?print.default __Note that for large values of digits, currently for digits >= 16, the calculation of the number of significant digits will depend on the platform's internal (C library) implementation of sprintf() functionality. __
  • Gabi
    Gabi over 8 years
    What's wrong with as.integer(x) == x? It will not reject 3 or 3.0 like is.integer(x) would, and it will catch 3.1.
  • Mehrad Mahmoudian
    Mehrad Mahmoudian over 8 years
    @VitoshKa loved your answer! Although there is one point that you missed, negative numbers without decimal points are also integer ;) I modified your code accordingly.
  • Alex
    Alex about 7 years
    all(a == as.integer(a)) gets around this problem!'
  • PeterVermont
    PeterVermont about 7 years
    The second line says "as.integer tests if the number is declared as an integer." but I am pretty sure you meant "is.integer". It is only a one character edit so I couldn't easily change it.
  • tstudio
    tstudio almost 6 years
    This is not working properly! Check out the following counter-example: frac_test <- 1/(1-0.98), all.equal(frac_test, as.integer(frac_test)), isTRUE(all.equal(frac_test, as.integer(frac_test)))
  • Corrado
    Corrado almost 5 years
    if x <- sqrt(2)^2, then all(floor(x) == x, na.rm = TRUE) return FALSE
  • green diod
    green diod almost 2 years
    @MehradMahmoudian How did you cope with negative numbers?