4

I'd appreciate any idea on how to do it, so we can compare them with each other.

Here is one to start out with:

is.natural <- function(x)
{
     x>0 && identical(round(x), x)
}
Braiam
  • 1
  • 11
  • 47
  • 78
Tal Galili
  • 24,605
  • 44
  • 129
  • 187

5 Answers5

5

The docs suggest a similar method, so I doubt you'll get any better. Remember to include an epsilon to take into account precision issues!

is.naturalnumber <-
    function(x, tol = .Machine$double.eps^0.5)  x > tol & abs(x - round(x)) < tol
is.naturalnumber(1) # is TRUE
(x <- seq(1,5, by=0.5) )
is.naturalnumber( x ) #-->  TRUE FALSE TRUE ...
moinudin
  • 134,091
  • 45
  • 190
  • 216
3

Be aware that identical() checks for storage type as well: your is.natural(1L) returns FALSE since typeof(1L) is integer but typeof(round(1L)) is double. As an alternative to marcog I suggest the following function which uses all.equal() to do the checking and handles complex numbers:

is.natural <- function(x, tol = .Machine$double.eps^0.5) {
    (abs(Im(x)) < tol) &&
    (abs(Re(x)) > tol) &&
    isTRUE(all.equal(x, round(x),
                     tolerance=tol,
                     check.attributes=FALSE,
                     check.names=FALSE))
}
caracal
  • 2,690
  • 19
  • 22
2

One more solution is using a bit of arithmetics :

is.natural2 <- function(x,tol=.Machine$double.eps^0.5){
     sign(x) - abs(x-round(x))  >= 1-tol
}

Now, when checking all solutions, it turned out that the one of Tal and the one of caracal didn't give the correct result:

is.natural <- function(x)
{
     x>0 & identical(round(x), x)
}

is.natural2 <- function(x,tol=.Machine$double.eps^0.5){
     sign(x) - abs(x-round(x))  >= 1-tol
}

is.natural3 <- function(x, dig=15){ x > 0 & zapsmall(x, dig) == round(x) }


is.natural4 <- function(x,tol=.Machine$double.eps^0.5){
     x > 0 & 
     isTRUE(all.equal(x,round(x),
                tolerance=tol,
                check.attributes=FALSE,
                check.names=FALSE))
}

is.naturalnumber <- function(x, tol = .Machine$double.eps^0.5){
     x > 0 & abs(x - round(x)) < tol
}

Then :

> X <- (seq(0,3,by=0.5)^0.5)^2

> is.natural(X)
[1] FALSE FALSE FALSE FALSE FALSE FALSE FALSE # wrong

> is.natural2(X)
[1] FALSE FALSE  TRUE FALSE  TRUE FALSE  TRUE

> is.natural3(X)
[1] FALSE FALSE  TRUE FALSE  TRUE FALSE  TRUE

> is.natural4(X)
[1] FALSE FALSE FALSE FALSE FALSE FALSE FALSE # wrong

> is.naturalnumber(X)
[1] FALSE FALSE  TRUE FALSE  TRUE FALSE  TRUE

Regarding the timing :

> X <- (seq(0,1000000,by=0.5)^0.5)^2

> system.time(is.natural2(X))
   user  system elapsed 
   0.24    0.03    0.26 

> system.time(is.natural3(X))
   user  system elapsed 
   0.67    0.00    0.67 

> system.time(is.naturalnumber(X))
   user  system elapsed 
   0.22    0.01    0.23 

which makes marcog the winner.

Joris Meys
  • 106,551
  • 31
  • 221
  • 263
2

You should always have integer size limit in mind: .Machine$integer.max. Even if you do some kind of "check", if given numerical value exceeds the limit, R will see it as double, and it is stored in a different manner.

> (x <- as.integer(.Machine$integer.max + 1))
[1] NA
Warning message:
NAs introduced by coercion
> (x <- as.double(.Machine$integer.max + 1))
[1] 2147483648
> typeof(x)
[1] "double"
> x <- 2147483647L
> typeof(x)
[1] "integer"
> x <- 2147483648L
Warning message:
non-integer value 2147483648 qualified with L; using numeric value
> typeof(x)
[1] "double"
aL3xa
  • 35,415
  • 18
  • 79
  • 112
  • +1 for point regarding integer.max but I cannot figure out why you are worrying about even-digit rounding conventions here. I would have thought that a discussion of the potential pitfalls regarding use of as.integer (which always truncates rather than rounding) would have been more germane. – IRTFM Dec 30 '10 at 21:16
1

You need two tests: greater than 0 and close enough to an integer. The identical() test fails too often because it also checks for extraneous attributes and storage mode. The zapsmall provides the ability to restrict rounding to a particular number of digits and does so in a manner that can be applied to vectors of candidate values. You can adjust the number of significant digits if desired.

is.natural <- function(x, dig=15){ x > 0 & 
                                   zapsmall(x, dig) == round(x) &
                                   x < .Machine$integer.max }

 x <- (2^0.5)^2
 x ==2
# [1] FALSE       (machine mathematics)
is.natural(x)
# [1] TRUE

Edit: A good point was made about checking for range, so that was added.

IRTFM
  • 258,963
  • 21
  • 364
  • 487