13

I think I'm going insane. "counter" and "interval" are both doubles. This is happening on accelerometer:didAccelerate at an interval of (.01) . "counter" should eventually increment to "interval". For some reason i cant get this "if" to ring true.

Am I overlooking something?

double interval = .5;
 if( counter == interval ){ //should eventually be .50000 == .50000
  NSLog( @"Hit!" );
  [self playSound];
  counter = 0;
 }else{
  counter += .01;
 }
NSLog( @"%f, %f, %d",counter,interval,(counter == interval) );
Stephen Canon
  • 103,815
  • 19
  • 183
  • 269
azeven
  • 131
  • 1
  • 1
  • 3
  • 1
    possible duplicate of [Trouble with Float in Objective-c \[SOLVED\]](http://stackoverflow.com/questions/1193554/trouble-with-float-in-objective-c-solved) – Seth Jan 19 '11 at 07:15
  • Yep, turns out to be just that. Though it seems to be the inverse problem, i used the float type and it worked. Thanks Seth – azeven Jan 19 '11 at 07:23
  • 1
    You should not rely on float/double. Using float may solve this one, but will break in another situation. Try to figure out better solutions. Using >= instead of == may solve in many cases. – taskinoor Jan 19 '11 at 07:31
  • Wow.. yeah, using float ended up not working either. Coming from AS3 and JS, I really had no idea. Thanks everyone. Gonna read the floating points wiki page a little harder. – azeven Jan 19 '11 at 08:05
  • 1
    Think of doubles and floats in terms of IEEE 754, not in terms of a particular language. The IEEE 754 doesn't guarantee that two floating point values will be equal (in the `==` sense) even though you expect them to have the same value. Use a delta-comparison like this: `if (fabs(counter - interval) < SOME_SMALL_ENOUGH_VALUE) ...;` – Alexei Sholik Jan 19 '11 at 08:05

2 Answers2

27

Don't ever compare doubles or floats with equality - they might look the same at the number of significant figures your are examining but the computer sees more.

For this purpose, the Foundation Framework provides "epsilon" values for different types such as "float" and "double". If the distance between two numbers is smaller than epsilon, you can assume these two numbers are equal.

In your case, you would use it as follow:

- (BOOL)firstDouble:(double)first isEqualTo:(double)second {
    if(fabs(first - second) < DBL_EPSILON)
        return YES;
    else
        return NO;
}

Or in Swift 4:

func doublesAreEqual(first: Double, second: Double) -> Bool {
    if fabs(first - second) < .ulpOfOne {
        return true
    }
    return false
}

Two very useful links:

What Every Computer Scientist Should Know About Floating-Point Arithmetic

Interesting discussion of Unit in Last Place (ULP) and usage in Swift

Friday Q&A 2011-01-04: Practical Floating Point

Adam Eberbach
  • 12,309
  • 6
  • 62
  • 114
2

In your else block, you are not adding 0.01 to counter, because that is not a representable double-precision value. You are actually adding the value:

0.01000000000000000020816681711721685132943093776702880859375

Unsurprisingly, when you repeatedly add this value to itself, you never get 0.5 exactly.

Two options: the better is to replace the if condition with (counter >= interval). Alternatively, you could use a small power of two for the increment instead of something that cannot be represented, like 0.0078125.

Stephen Canon
  • 103,815
  • 19
  • 183
  • 269