[R] The elegant way to test if a number is a whole number

David Winsemius dwinsemius at comcast.net
Thu Sep 8 22:56:40 CEST 2011


On Sep 8, 2011, at 4:41 PM, Marc Schwartz wrote:

>
> On Sep 8, 2011, at 3:09 PM, David Winsemius wrote:
>
>>
>> On Sep 8, 2011, at 3:54 PM, Marc Schwartz wrote:
>>
>>>
>>> On Sep 8, 2011, at 2:42 PM, David Winsemius wrote:
>>>
>>>>
>>>> On Sep 8, 2011, at 3:35 PM, Alexander Engelhardt wrote:
>>>>
>>>>> Am 08.09.2011 20:48, schrieb Marc Schwartz:
>>>>>> There was a post from Martin Maechler some years ago and I had  
>>>>>> to search a bit to find it. For these sorts of issues, I  
>>>>>> typically trust his judgement.
>>>>>>
>>>>>> The post is here:
>>>>>>
>>>>>> https://stat.ethz.ch/pipermail/r-help/2003-April/032471.html
>>>>>>
>>>>>> His solution also handles complex numbers.
>>>>>
>>>>> For those too lazy to follow
>>>>> He is basically creating the function is.whole:
>>>>>
>>>>> is.whole <- function(x)
>>>>>  is.numeric(x) && floor(x)==x
>>>>
>>>> Are you sure? I thought the test would have been all.equal(x,  
>>>> round(x,0) )
>>>>
>>>> My reasoning was that 1.999999999999 should be considered 2 but  
>>>> floor would make it 1.
>>>
>>> David, I am confuzzled. Why would that be equal to 2?
>>
>> So that sqrt(3) * sqrt(3) would be a "whole number". (It is true  
>> the the floor based wholeness criterion would make sqrt(2)*sqrt(2)
>>
>> Somehow it doesn't see "right" that only half of square roots of  
>> integers that have been squared should pass the wholeness test:
>>
>>> is.whole <- function(a, tol = 1e-7) {
>> +    is.eq <- function(x,y) {
>> + 	 r <- all.equal(x,y, tol=tol)
>> + 	 is.logical(r) && r
>> +    }
>> +    (is.numeric(a) && is.eq(a, floor(a))) ||
>> +    (is.complex(a) && {ri <- c(Re(a),Im(a)); is.eq(ri, floor(ri))})
>> + }
>>> is.whole( sqrt(2)^2 )
>> [1] TRUE
>>> is.whole( sqrt(3)^2  )
>> [1] FALSE
>>
>
> <snip content>
>
> OK. I suspect it may down to what assumptions one is willing to  
> make, including the level of tolerance used for the comparison.

Out of interest I tested whether my guess that half of the squared- 
sqrt's would fail and it was not even close to 50%. So I took the  
testing further and cannot explain the results:

 > sum(sapply(sqrt(2:10000)^2, is.whole))
[1] 7577
 > sum(sapply(((2:10000)^(1/3) )^3, is.whole))
[1] 49                                # No that _is_ strange.
 > sum(sapply(((2:10000)^(1/4) )^4, is.whole))
[1] 6207
 > sum(sapply(((2:10000)^(1/5) )^5, is.whole))
[1] 9946
 > sum(sapply(((2:10000)^(1/6) )^6, is.whole))
[1] 1324

Pondering the irregular pattern of results above is probably entirely  
tangential. I think that if a number "should" have been an integer it  
should have been defined as an integer and otherwise being close  
enough on either side of an integer value should be enough.

  is.whole2 <- function(a, tol = 1e-7) {
     is.eq <- function(x,y) {
  	 r <- all.equal(x,y, tol=tol)
  	 is.logical(r) && r
     }
     (is.numeric(a) && is.eq(a, round(a,0))) ||
     (is.complex(a) && {ri <- c(Re(a),Im(a)); is.eq(ri, floor(ri))})
  }

 > sum(sapply(((2:10000)^(1/3) )^3, is.whole2))
[1] 9999
 > sum(sapply(((2:10000)^(1/5) )^5, is.whole2))
[1] 9999


>
> is.whole() of course works for 2 because:
>
>> print(sqrt(2) ^ 2, 20)
> [1] 2.0000000000000004441
>
> is slightly larger than 2, so:
>
>> floor(sqrt(2) ^ 2)
> [1] 2
>
> works, as does:
>
>> round(sqrt(2) ^ 2, 0)
> [1] 2
>
>
> On the other hand:
>
>> print(sqrt(3) ^ 2, 20)
> [1] 2.9999999999999995559
>
> is slightly smaller than 3, so:
>
>> floor(sqrt(3) ^ 2)
> [1] 2
>
> versus:
>
>> round(sqrt(3) ^ 2, 0)
> [1] 3
>
>
> Not sure if Martin (cc'd now) might have any comments 8 plus years  
> later relative to this issue, as I would again defer to his  
> judgement here.
>
> The other solution proposed, using modulo division, would logically  
> fail in both cases:
>
>> (sqrt(3) ^ 2) %% 1 == 0
> [1] FALSE
>
>> (sqrt(2) ^ 2) %% 1 == 0
> [1] FALSE
>
>
> Regards,
>
> Marc
>

David Winsemius, MD
West Hartford, CT



More information about the R-help mailing list