Home > database >  Calculation bug in R
Calculation bug in R

Time:02-06

I noticed, that the following check is giving FALSE in R:

(1000*0.6/24)==(1000*(0.6/24))

[1] FALSE

as follow up the floor function is giving different results:

floor(1000*0.6/24)
[1] 25

floor(1000*(0.6/24))
[1] 24

This inconsistency is critical for my code.

Does somebody have an explanation and a tip on how to prevent this behavior?

Thank you.

Best regards,

John

CodePudding user response:

This is a frequently asked question -- see FAQ 7.31. It is due to the fact that floating point in R and all other computer languages has a finite number of digits of precision. You can use all.equal. Note that:

  1. the numeric method of all.equal has a tolerance argument which defaults to sqrt(.Machine$double.eps) but can be set to other values.

  2. all.equal returns TRUE if the two arguments are within the tolerance but does not return FALSE otherwise so we use isTRUE to ensure a logical result.

Thus

isTRUE(all.equal( (1000*0.6/24), (1000*(0.6/24)) ))
## [1] TRUE

isTRUE(all.equal(1, 2))
## [1] FALSE

You can also do it like this:

abs( (1000*0.6/24) - (1000*(0.6/24)) ) < sqrt(.Machine$double.eps)
## [1] TRUE

or possibly just choose a value:

abs( (1000*0.6/24) - (1000*(0.6/24)) ) < 1e-8
## [1] TRUE

You may also want to look at https://or.stackexchange.com/questions/443/modeling-floor-function-exactly

CodePudding user response:

First lest look at the R documentation for this functions:

S4 methods These are all (internally) S4 generic.

ceiling, floor and trunc are members of the Math group generic. As an S4 generic, trunc has only one argument.

round and signif are members of the Math2 group generic.

Warning The realities of computer arithmetic can cause unexpected results, especially with floor and ceiling. For example, we ‘know’ that floor(log(x, base = 8)) for x = 8 is 1, but 0 has been seen on an R platform. It is normally necessary to use a tolerance.

Now if you run the experiment using round, you go the following:

> floor(1000*(0.6/24))
[1] 24
> 
> floor(1000*0.6/24)
[1] 25
> 
> round(1000*(0.6/24))
[1] 25
> 
> round(1000*0.6/24)
[1] 25
> 
> ceiling(1000*(0.6/24))
[1] 25
> 
> ceiling(1000*0.6/24)
[1] 25
>

Conclusion: ceiling is working but as the documentation suggests, can also fail as floor. Round seems to be a better option here.

  •  Tags:  
  • Related