2013-04-09 3 views
0

Я пытаюсь проверить, является ли число чисто делимым (без остатков) другим. Если сравнивать его против междунар, он работает правильно:Как проверить, является ли double с десятичными знаками делимыми?

NSTimeInterval timeElapsed = // a double goes here 

// round to 3 decimal points to avoid floating point weirdness 
timeElapsed = [[NSString stringWithFormat:@"%.3f",timeElapsed] doubleValue]; 

, а затем

// This works correctly 

if (fmod(timeElapsed, 1) == 0) { 
      NSLog(@"is divisible"); 
} 

Но если сравнить его с десятичной, он терпит неудачу, выпустив только intermittedly:

// This does NOT work correctly 

if (fmod(timeElapsed, .1) == 0) { 
      NSLog(@"is divisible");  
} 

Что я делаю неправильно?

EDIT: После прочтения ответа и замечательных комментариев ниже я пришел к выводу, что весь этот подход - плохая идея. Нет никакой гарантии, что вы сможете создавать определенные временные интервалы. Вероятно, лучше просто создать отдельные таймеры вместо того, чтобы слушать один «главный» таймер.

+0

Что именно не подходит для вас? – giorashc

+0

@giorashc Если, например, 'timeElapsed' равно 3.400, он должен пройти тест, потому что он должен быть чисто делимым на' .1'. Но это не так. – zakdances

+0

Пол R имеет очень хороший момент в этом вопросе. Насколько я понимаю, вы пытаетесь сделать что-то каждые 0,1 сек? – giorashc

ответ

0

Это ужасный подход. Нельзя доверять номерам с плавающей запятой. Дайте каждому элементу свой собственный таймер, чтобы вам не приходилось прослушивать определенные числа.

3

В целом числа с плавающей запятой не являются точно представляемыми, поскольку у вас есть только конечное число бит, с помощью которых они могут быть представлены (обычно 32 или 64). Поэтому вы никогда не должны проверять точное равенство, но вместо этого проверяйте с приемлемым небольшим допуском, например.

if (fabs(fmod(timeElapsed, .1)) < SOME_SMALL_VALUE) { 
    NSLog(@"is divisible");  
} 

Это хорошая идея, чтобы прочитать Goldberg paper on floating point arithmetic если вы собираетесь писать какой-либо серьезный числовой код.

+0

Возможно, я должен был добавить это к моему примеру, но перед тем, как сравнить его, я округляю 'timeElapsed' до 3 десятичных точек. – zakdances

+1

Если NSTimeInterval - это значение с плавающей запятой, то неважно, закругляете его или нет. –

+0

Общим значением, используемым для «SOME_SMALL_VALUE», является FLT_EPSILON. Вы можете увидеть эффект, который Павел обсуждает, если вы распечатаете значение fmod (timeElapsed). 0,1 не может быть точно выражен в двоичном формате, поэтому невозможно применить fmod() к нему и получить точные значения. http://www.wolframalpha.com/input/?i=0.1+to+binary –

1

Если вы хотите, чтобы выполнить задачу через 0,1 секунды повторно использовать NSTimer:

NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:0.1 
      target:self 
      selector:@selector(yourMethod:) 
      userInfo:nil 
      repeats:YES]; 

Чтобы остановить ВЫЗОВ таймера:

[timer invalidate]; 
+0

NSTimer может быть лучшим подходом, но есть предостережение из [Apple docs] (https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSTimer_Class/Reference/NSTimer. html): 'Если время срабатывания таймера происходит во время длинной выноски или в то время, когда цикл цикла находится в режиме, который не контролирует таймер, таймер не срабатывает до следующего цикла проверки таймера. Следовательно, фактическое время, в течение которого таймер срабатывает потенциально, может быть значительным периодом времени после запланированного времени стрельбы. «В любом случае у вас будет такая же проблема с вашим текущим методом. –

Смежные вопросы