Take the 2-minute tour ×
Mathematica Stack Exchange is a question and answer site for users of Mathematica. It's 100% free, no registration required.

The following is the program.

test[t_, dt_] := 
 Module[{}, For[ti = dt, ti <= t, ti = ti + dt, Print[ti];]; 
  Print[MemberQ[{0.01, 0.02}, ti]]; Return[0];]    
test[0.01, 0.001]

(* 0.001
...
False
0 *)

Obviously the result is wrong. Copy the above result, you will be surprised to see:

(* ...
0.009000000000000001`
0.010000000000000002`
... *)

Why is this so?

share|improve this question
1  
This is not a Mathematica question - it is a numerical analysis question: en.wikipedia.org/wiki/Round-off_error –  Mark McClure May 15 '12 at 12:41
5  
It's not the For[] loop, really. Try 0.008 + 0.001 // InputForm. Welcome to the fun of floating-point arithmetic! –  J. M. May 15 '12 at 12:41
    
Good,Thank you! –  howard May 15 '12 at 13:16
    
What's the point of Module[{}, ...] with empty variable list? As far as I understand, it should not have any effect at all. –  celtschk May 16 '12 at 13:22
    
@celtschk I've used it a few times as a cool parentheses, reserved for future use... Then I stopped doing that –  Rojo May 16 '12 at 18:34

2 Answers 2

While it is a bad idea to compare floating point numbers, in this case I think something simpler is going on: you have an off-by-1 problem in your loop. See what your code does:

test[t_, dt_] := Module[{},
  For[ti = dt, ti <= t, ti = ti + dt, Print[ti];];
  Print[MemberQ[{0.01, 0.02}, ti]];
  Return[0];
  ]

then, after

test[0.01, .001];

look at ti:

ti
(*0.011*)

If you use < rather than <= in the condition, the final value will indeed be what you expect it to be:

test2[t_, dt_] := Module[{},
  For[ti = dt, ti < t, ti = ti + dt, Print[ti];];
  Print[MemberQ[{0.01, 0.02}, ti]];
  Return[0];
  ]

test2[0.01, .001]
ti

prints a list up to 0.009 and returns True.

In general, though, don't do things like that with floating-point numbers. Even if it works here, it will eventually fail. Observe:

(1 + $MaxMachineNumber) == $MaxMachineNumber
(*True*)

or, as JM points out,

1 + $MachineEpsilon == 1
(*True*)
share|improve this answer
2  
The more traditional example is 1 + $MachineEpsilon == 1... ;) –  J. M. May 15 '12 at 12:59
1  
@J.M. Tradition is for wimps :) –  acl May 15 '12 at 13:01
5  
Bear in mind Equal applies an extra tolerance in Mathematica. The proper comparison is Block[{Internal`$EqualTolerance = -Infinity}, 1 == 1 + $MachineEpsilon] (= False) vs. Block[{Internal`$EqualTolerance = -Infinity}, 1 == 1 + $MachineEpsilon/2] (= True). –  Oleksandr R. May 15 '12 at 13:55
    
@OleksandrR. I didn't know that, thanks –  acl May 15 '12 at 14:05

This seems to work fine and is more in the spirit of the Mathematica way and perhaps illustrates why a Functional approach tends to lead to fewer bugs than the procedural approach:

test[t_, dt_] := {#, MemberQ[{0.01, 0.02}, #]} & /@ Range[dt, t, dt];

Which when run,

test[0.01, 0.001] // TableForm

gives:

Mathematica graphics

share|improve this answer
1  
Replacing TableForm with InputForm reveals that one does not totally avoid floating-point error... –  J. M. May 15 '12 at 13:55
    
@J.M. that's interesting, it makes me curious as to why that happens, the increment doesn't seem to be anywhere near the limit of precision of of a floating point number. Is this a general effect with FP numbers or more specific to MMA? –  image_doctor May 15 '12 at 15:26
3  
it's general, see e.g. floating-point-gui.de –  acl May 15 '12 at 15:45
    
@acl, Thanks, surprising how in a great number of years that's never knowingly tripped me up, but here because MemberQ is being used as a criterion, rather than a < or > based test, it become obvious. –  image_doctor May 15 '12 at 16:00

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.