-1

I've been coding in R for years and something really weird is just happening and is driving me crazy..

I have this simple loop:

st=0
for (i in 1:20){
    if(st==0.3){print("Test passed")}
    st=st+0.1
}

Somehow, no idea why, the condition st==0.3 never hits True, i can print st at every step of the loop and it in facts becomes 0.3 at the proper step, but it's not triggering the condition. I've tried with other values and it seems to work, bun not with 0.3.

Now, this is just a "verification" example i made to test the problem, the original happened in another loop where a similar if is not getting triggered with many more values and i checked the variable takes those values at the corresponding steps and nothing.

I suspect, i don't know, maybe some kind of internal single precission is giving a slighty different number and is not being shown, or something like that.

Any idea what can be happening?

Ghost
  • 1,426
  • 5
  • 19
  • 38
  • 2
    This is a numeric rounding error. If you print "st" out to full precision it will most likely equal 0.2999999999998. There are many other question here discussing this problem. – Dave2e Jun 22 '20 at 15:24
  • 1
    instead of `st == 0.3` you can use `identical(st, 0.3)` – Cettt Jun 22 '20 at 15:27
  • 2
    Nope, @Cettt, that's effectively the same thing. Fundamentally, floating point tests of *equality* are imperfect/flawed; they work much of the time, but they are almost certainly going to fail at some point. – r2evans Jun 22 '20 at 15:28
  • 1
    Ghost, try `st <- 0; for (i in 1:20) { if (abs(st - 0.3) < 1e-8) print("Test passed"); st <- st + 0.1; }`, it'll get the effect you seem to need here. In the end, any floating-point comparisons need to allow for a tolerance; in this example I used 1e-8, a number well below most of my needs for precision. Please make sure that it is appropriate for you before using it blindly. – r2evans Jun 22 '20 at 15:31
  • 1
    Funny thing (on my machine): `(0 + 0.1 + 0.1 + 0.1) == 0.3` returns `FALSE, `(0 + 0.1 + 0.1 + 0.1) >= 0.3` returns `TRUE`, `(0 + 0.1 + 0.1 + 0.1) >= 0.3 + .Machine$double.eps` returns `FALSE`. – Martin Gal Jun 22 '20 at 15:38
  • 2
    @r2evans thank you for pointing that out. This works `if (isTRUE(all.equal(st, 0.3)))` – Cettt Jun 22 '20 at 15:41
  • 2
    If you look under the hood, `all.equal.numeric` uses a tolerance (it uses `sqrt(.Machine$double.eps)`), so same effect. I tend to work in larger datasets, so I found `abs(. - val) < 1e-8` to be much faster than a one-by-one `all.equal` comparison, but that's just my perspective. – r2evans Jun 22 '20 at 15:44
  • Thanks for the answers guys, in fact was a precision problem; it has never happened to me before, at least not in R and the print wasn't either showing the precision issue. Any of the methods provided work as long as they round the number. Thanks a lot again! – Ghost Jun 22 '20 at 15:49

1 Answers1

2
> st=0
> for (i in 1:20){
+   if(st==0.3){print("Test passed")}
+   st=round(st+0.1, 1)
+ }
[1] "Test passed"

So is it just numerical rounding/precision?

Limey
  • 10,234
  • 2
  • 12
  • 32
  • Well, tried that and worked, same as Dave2e and Chris comments, but the weird thing is that precision problem has never happened to me in R before, and i've made a million of these conditions. – Ghost Jun 22 '20 at 15:29
  • 3
    Everyone gets bitten by this particular dog at some point or another. It's just that today is your turn. Wear that tshirt with pride! :) – Limey Jun 22 '20 at 15:30
  • I have learned over the years that floating-point test of equality is fundamentally flawed in almost every programming language, as it is a property of storing floating-point numbers in a fixed-digit collection of bytes. IEEE-754 is the underlying "doctrine", and in the end, the only tests that one can use with high-confidence are tests of strict inequality. – r2evans Jun 22 '20 at 15:33
  • Awesome @Limey. I think many good programmers have missed this. There is a higher chance of encountering precision issues if `st` is a calculated value. Rounding to an appropriate level works most of the time. I have seen this issue in other languages too. – YBS Jul 14 '20 at 14:25