53 * .01 = .531250 - PullRequest
       16

53 * .01 = .531250

2 голосов
/ 09 сентября 2010

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

Моя дата записана в строковом формате @ "2010-09-08T17: 33: 53 + 0000". Поэтому я написал этот маленький метод для возврата значения времени. До того, как кто-то подскакивает на сколько секунд в месяцах с 28 днями или 31 днем, мне все равно. В моей математике хорошо предположить, что все месяцы имеют 31 день, а годы - 31 * 12 дней, потому что мне не нужна разница между двумя моментами времени, только чтобы узнать, является ли один момент времени позже другого.

-(float) uniqueTimeFromCreatedTime: (NSString *)created_time {
float time;
    if ([created_time length]>19) {
      time = ([[created_time substringWithRange:NSMakeRange(2, 2)]floatValue]-10) * 535680; // max for 12 months is 535680.. uh oh y2100 bug!
      time=time + [[created_time substringWithRange:NSMakeRange(5, 2)]floatValue] * 44640; // to make it easy and since it doesn't matter we assume 31 days
      time=time + [[created_time substringWithRange:NSMakeRange(8, 2)]floatValue] * 1440;
      time=time + [[created_time substringWithRange:NSMakeRange(11, 2)]floatValue] * 60;
      time=time + [[created_time substringWithRange:NSMakeRange(14, 2)]floatValue];
      time = time + [[created_time substringWithRange:NSMakeRange(17, 2)]floatValue] * .01;
      return time;
    }
    else {
      //NSLog(@"error - time string not long enough");
      return 0.0;
    }
}

При передаче той самой строки, указанной выше, результат должен быть 414333.53, но вместо этого он возвращает 414333.531250.

Когда я каждый раз бросаю NSLog =, чтобы отследить, где он происходит, я получаю такой результат:

time 0.000000
time 401760.000000
time 413280.000000
time 414300.000000
time 414333.000000
floatvalue 53.000000
time 414333.531250
Created Time: 2010-09-08T17:33:53+0000 414333.531250

Так что последнее значение floatValue вернуло 53.0000, но когда я умножил его на 0,01, оно превратилось в .53125. Я также попробовал intValue, и он сделал то же самое.

Ответы [ 5 ]

10 голосов
/ 09 сентября 2010

Добро пожаловать в ошибки округления с плавающей запятой. Если вам нужна точность два с фиксированным числом десятичных знаков, умножьте на 100 (для 2 десятичных знаков) затем round() и разделите на 100. Пока число не слишком велико (занимает больше, чем я думаю, 57 бит) ) тогда вы должны быть в порядке и не иметь никаких проблем с округлением при делении вниз.

РЕДАКТИРОВАТЬ: мое примечание о 57 битах должно быть отмечено, я предполагал, что двойной, с плавающей точкой гораздо меньше точности Сделайте так, как предлагает другой читатель, и переключитесь на удвоение, если это возможно.

6 голосов
/ 09 сентября 2010

IEEE имеет только 24 эффективных бита мантиссы (примерно от 7 до 8 десятичных цифр). 0,00125 является 24-й ошибкой округления между 414333,53 и ближайшим представлением с плавающей запятой, поскольку точное число 414333,53 требует 8 десятичных цифр. 53 * 0.01 само по себе будет намного точнее, прежде чем вы добавите его к большему числу и потеряете точность в полученной сумме. (Это показывает, почему сложение / вычитание между числами очень разных размеров нехорошо с цифровой точки зрения при вычислении с арифметикой с плавающей запятой.)

2 голосов
/ 09 сентября 2010

Вы можете создать NSDate экземпляров из этих NSString дат, используя метод +dateWithString:. Он принимает строки в формате YYYY-MM-DD HH:MM:SS ±HHMM, с чем вы имеете дело. Если у вас есть два NSDate с, вы можете использовать метод -compare:, чтобы узнать, какой из них будет позже.

2 голосов
/ 09 сентября 2010

Это из классической ошибки с плавающей запятой, возникающей из-за того, как число представляется в битах. Во-первых, используйте double вместо float, так как его достаточно быстро использовать на современных машинах. Когда результат действительно имеет значение, используйте десятичный тип, который в 20 раз медленнее, но точен на 100%.

1 голос
/ 09 сентября 2010

Вы можете попробовать умножить все свои константы на 100, чтобы вам не приходилось делить. Разделение - это то, что вызывает проблему, потому что деление на 100 создает повторяющийся шаблон в двоичном формате.

...