0

So let's start with normal values in the regular R setup:

n1 = 0.15;
n2 = 0.15;

n1 == n2;  # we expect and get TRUE

Now, let's update our digits to 22 using options

options(digits=22);

n1 == n2; # we expect and still get TRUE but begin to worry

# our data has changed

> n1
[1] 0.14999999999999999
> n2
[1] 0.14999999999999999

I am on Windoze 10, running 64-bit.

Now let's create a vector or import a data.frame or do something where one of the elements theoretically is 0.15 ...

vec =  seq(0,1, by=0.05);

vec;

 [1] 0.000000000000000000 0.050000000000000003 0.100000000000000006
 [4] 0.150000000000000022 0.200000000000000011 0.250000000000000000
 [7] 0.300000000000000044 0.350000000000000033 0.400000000000000022
[10] 0.450000000000000011 0.500000000000000000 0.550000000000000044
[13] 0.600000000000000089 0.650000000000000022 0.700000000000000067
[16] 0.750000000000000000 0.800000000000000044 0.850000000000000089
[19] 0.900000000000000022 0.950000000000000067 1.000000000000000000

And finally, compare the vector to one of the n's

vec == n1;

 [1] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
[13] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE


Oh snap! After several hours of debugging I found this (in the subsetting context).

Sometimes, depending on how the vector is built the comparison will work correctly. Sometimes, like in this example it will not.

> n1;
[1] 0.14999999999999999
> n2;
[1] 0.14999999999999999
> vec[4];
[1] 0.15000000000000002


> n1 == vec[4];
[1] FALSE
> n1 == n2;
[1] TRUE

If we could apply the Math.tolerance from the GLOBAL ENV, they are equal. My work around was to manually build a tolerance and replace the == operation with a joint >lowerbound | <upperbound using my own tolerance.

Since I believe this is due to floating-point issues, I was hoping for some insight from others.

How can I compare two numerics x==y where I get true even if the float-point issues says they may not be?

mshaffer
  • 959
  • 1
  • 9
  • 19

1 Answers1

1

all.equal might be useful here:

n1 <- 0.14999999999999999
n2 <- .15
n3 <- 0.15000000000000002

all.equal(n1,n2)
# [1] TRUE

all.equal(n1,n3)
# [1] TRUE

You can manually specify a tolerance if you like, e.g.,

all.equal(n1, n3, tolerance = 1.5e-16)
# [1] "Mean relative difference: 1.850372e-16"

Finally, as the help page for all.equal says, if you need a bool returned, wrap it in isTRUE(all.equal(...)) or identical.

heds1
  • 3,203
  • 2
  • 17
  • 32
  • Thanks. Per the manual: `isTRUE(x) is the same as { is.logical(x) && length(x) == 1 && !is.na(x) && x }` so I guess `all.equal` may return NULL sometimes. – mshaffer Sep 08 '20 at 19:16