какао - я обнаружил, что я думаю, это ошибка с NSDecimalNumber - PullRequest
5 голосов
/ 19 марта 2010

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

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

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

+36,76662445068359375000 и

36,76662445068359168

Это ошибка или я что-то упустил?

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

= = = = = = = = = =

Я редактирую оригинальный вопрос, чтобы включить еще одну ошибку 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 теперь имеет больше цифр !!!!

Ответы [ 2 ]

10 голосов
/ 19 марта 2010

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

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

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

double wtf = 36.76662445068359375000;

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

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

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

2 голосов
/ 19 марта 2010

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

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

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

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...