1

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

John
  • 69
  • 5

3 Answers3

5

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

G. Grothendieck
  • 254,981
  • 17
  • 203
  • 341
  • Small correction: floating point *arithmetic* is 100% exact (unless using broken tools, such as the `-ffast-math` option on GCC). It’s just that fixed-size fractions fundamentally can’t represent all rational (let alone real) numbers. – Konrad Rudolph Feb 05 '22 at 14:38
0

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.

AugtPelle
  • 549
  • 1
  • 10
-1

Thank you very much for your proposals.

My actual problem was that

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

The issue can be solved by dividing/multiplicating both sides with enough big number.

Example with 100:

> floor(1000*100*(0.6/24/100))
[1] 25
John
  • 69
  • 5
  • I'm not sure that this will consistently solve this problem for you – Captain Hat Feb 07 '22 at 10:49
  • It does, you just need to use a big enough multiplicator. – John Feb 07 '22 at 10:55
  • That's not true. On my machine `floor(1000*100*(0.6/24)/100)` comes out at **25**, but `floor(1000*1000*(0.6/24)/1000)` comes out at **24**. Changing the equation changes the binary value, but not in the predictable way you assert here. – Captain Hat Feb 07 '22 at 11:08