какао - я обнаружил то, что я думаю, ошибка с NSDecimalNumber

Вот простой код, который показывает то, что я думаю, ошибка при контакте с двойными числами...

double wtf = 36.76662445068359375000;
id xxx = [NSDecimalNumber numberWithDouble: wtf];
NSString *myBug = [xxx stringValue];
NSLog(@"%.20f", wtf);
NSLog(@"%@", myBug);
NSLog(@"-------\n");

терминал покажет два различных числа

36.76662445068359375000 и

36.76662445068359168

Действительно ли это - ошибка, или я пропускаю что-то?

если второе число округляется, это - очень странное округление btw...

= = = = = = = = = =

Я редактирую исходный вопрос включать еще одну ошибку WTF...

попробуйте это:

измените исходное число и усеките его на 10 десятичных цифрах... так...

double wtf = 36.76662445068359375000;
NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
[formatter setMaximumFractionDigits:10];

NSString *valueX = [formatter stringFromNumber:[NSDecimalNumber numberWithDouble:wtf]];
[formatter release];
NSLog(@"%@", valueX);

ответ теперь 36.7666244507

теперь значение является строкой с 10 десятичными цифрами..., теперь позволяет, преобразовывают его назад для удвоения

double myDoubleAgain = [valueX doubleValue];
NSLog(@"%.20f", myDoubleAgain);

ответ 36.76662445070000018177??????

myDoubleAgain имеет теперь больше цифр!!!!

5
задан SpaceDog 19 March 2010 в 18:21
поделиться

2 ответа

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

Этот вопрос гораздо веселее, чем то, что мы обычно видим, и он точно иллюстрирует, что не так с распространенной мудростью «плавающая точка неточна, прочтите ' то, что должен знать каждый компьютерный ученый ... » лолз ".

36.76662445068359375 - это не просто 19-значное десятичное число. Это 19-значное десятичное число, которое также может быть точно представлено в двоичной системе с плавающей запятой двойной точности. Таким образом, начальное преобразование, неявное в:

double wtf = 36.76662445068359375000;

, является точным. wtf содержит точно b100100.11000100010000011 , и округления не произошло.

В спецификации для NSDecimalNumber говорится, что он представляет числа в виде 38-значной десятичной мантиссы и десятичной экспоненты в диапазоне [-127,128], поэтому значение в wtf также точно может быть представлено как NSDecimalNumber. Таким образом, мы можем сделать вывод, что numberWithDouble не выполняет правильное преобразование. Хотя я не могу найти документацию, в которой утверждается, что эта процедура преобразования округлена правильно, нет веских причин, чтобы этого не было. Это настоящая ошибка , сообщите об этом .

Я заметил, что средства форматирования строк в iPhoneOS, похоже, дают правильно округленные результаты, поэтому вы, вероятно, можете обойти это, сначала отформатировав двойное число как строку с точностью до 38 цифр, а затем используя decimalNumberWithString .Не идеально, но может сработать для вас.

10
ответ дан 13 December 2019 в 05:33
поделиться

Пытаться выйти за пределы примерно 16 цифр десятичной точности с double обычно не рекомендуется. Честно говоря, я удивлен, что вы смогли представить этот double (с 19 значащими цифрами) таким образом, чтобы сохранить эту точность при его регистрации. Вы даже можете получить другое поведение на самом iPhone, который отображает тип long double на обычный double (ваш Mac может обрабатывать это как long double за кулисами).

Округление, которое вы видите, может происходить на двоичном уровне (подробнее об этом см. здесь), поэтому вы не увидите десятичного округления, которого вы ожидаете.

Именно по этим причинам вы захотите от начала до конца работать с NSDecimalNumbers или NSDecimals, если вам нужна такая высокоточная математика. Для этого не конвертируйте в типы с плавающей точкой и обратно, а вместо этого используйте непосредственно NSStrings для заполнения и экспорта чисел (или храните их как NSDecimalNumbers в Core Data).

Например, вы можете обойти вышеуказанные проблемы с помощью следующего кода:

id xxx = [NSDecimalNumber decimalNumberWithString:@"36.76662445068359375000"];

NSDecimalNumbers (и их эквивалент NSDecimal в структуре C) могут обрабатывать до 38 значащих цифр.

2
ответ дан 13 December 2019 в 05:33
поделиться
Другие вопросы по тегам:

Похожие вопросы: