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

Martin Maechler maechler at stat.math.ethz.ch
Fri Sep 9 10:41:10 CEST 2011

>>>>> Marc Schwartz <marc_schwartz at me.com>
>>>>>     on Thu, 8 Sep 2011 15:41:26 -0500 writes:

> 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.

thank you, Marc!   :-)

>>>>> 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) )

Yes, I was *very* sure that  the is.whole() above should be
exactly what it was:

The purpose was really different.
There, the discussion started from the fact that
as.integer(1) gives FALSE in R
and that some people where making a fuzz about the allegedly
poor design of S and R in that regard.

The purpose of the above  is.whole()
was solely to be a
concise  *alternative* to is.integer(),
-- which even would work with complex numbers, as Marc
mentioned...
and BTW, instead of floor(), trunc(), or ceiling() or even round()
would have been equally valid.  I had chosen floor() because  in
mathematics, it is the most frequently used of those four functions.

floating point arithmetic is an entirely different issue,
*and* I agree that a solution to your problem should be based on
the same calculations as all.equal.numeric() .. and hence will
be considerably more involved if it should "work" in all border
cases.

Martin Maechler, ETH Zurich

>>>> 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.

> 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