Check if the number is integer
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.
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, 2021Comments
-
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 ifx
contains integer numbers! For that, useround
, as in the functionis.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 over 13 yearsjust 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 over 13 yearsI'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 about 12 yearsThis doesn't look very reliable to me:
check.integer(1e4)
is TRUE, whilecheck.integer(1e5)
is FALSE. -
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 about 11 yearsThe last
if
else
could be done with simplyisTRUE(test)
. Indeed that is all you need to replace theif
else
clause and thereturn
statements as R automatically returns the result of the last evaluation. -
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 about 11 yearsAnd 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 over 10 yearsdoes the tolerance-checking suggestion really work??
x <- 5-1e-8; x%%1
gives 0.9999999 (which would imply iftol==1e-5
for example) thatx
is not an integer. -
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 almost 9 years
testInteger(1.0000001)
[1] FALSEtestInteger(1.00000001)
[1] TRUE -
PatrickT almost 9 yearscheck.Integer(1.000001) [1] FALSE check.Integer(1.0000001) [1] TRUE
-
Cath almost 9 years@James, I think it should be
min(abs(c(x%%1, x%%1-1))) < tol
instead ofabs(min(x%%1, x%%1-1)) < tol
otherwise, you'll getFALSE
for any integer... -
PatrickT almost 9 yearsIt does here. I just checked again. I can't explain it. Here a screenshot: postimg.org/image/xrbx1dlen
-
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 almost 9 years@VitoshKa, much better, but there's a break at 15 digits, not 20 - is that expected?
check.integer(1.000000000000001)
[1] FALSEcheck.integer(1.0000000000000001)
[1] TRUE -
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 formcheck.integer("1000000000000000000000000000000000001") [1] TRUE
-
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 almost 9 years@PatrickT. That's precisely what
format
does. It converts to character. -
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 over 8 yearsWhat's wrong with
as.integer(x) == x
? It will not reject 3 or 3.0 likeis.integer(x)
would, and it will catch 3.1. -
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 about 7 years
all(a == as.integer(a))
gets around this problem!' -
PeterVermont about 7 yearsThe 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 almost 6 yearsThis 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 almost 5 yearsif
x <- sqrt(2)^2
, thenall(floor(x) == x, na.rm = TRUE)
returnFALSE
-
green diod almost 2 years@MehradMahmoudian How did you cope with negative numbers?