关于iphone:cocoa – 我发现了我认为是NSDecimalNumber的错误

cocoa - I've discovered what I think is a bug with NSDecimalNumber

下面是一个简单的代码,它显示了我认为处理双精度数时的错误…

1
2
3
4
5
6
7
double wtf = 36.76662445068359375000;
id xxx = [NSDecimalNumber numberWithDouble: wtf];
NSString *myBug = [xxx stringValue];
NSLog(@"%.20f", wtf);
NSLog(@"%@", myBug);
NSLog(@"-------
");

终端将显示两个不同的数字

36.76662445068359375000和

36.76662445068359168

这是虫子还是我丢了什么东西?

如果第二个数字是四舍五入的,这是一个非常奇怪的四舍五入。

==========

我正在编辑原始问题以包含一个以上的wtf错误…

试试这个:

修改原始数字并将其截断为10位十进制数字…所以…

1
2
3
4
5
6
7
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位小数的字符串…现在让我们把它转换回双精度

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

答案是36.7666244507000018177????????

我的双翼现在有更多的数字了!!!!!


通常情况下,我是那个进来向人们解释他们输入的数字不能表示为浮点数的人,而舍入错误在哪里,等等。

这个问题比我们通常看到的有趣得多,它确切地说明了"浮点不精确,读‘每一个计算机科学家应该知道什么…’lolz’这一群体智慧的错误所在。

36.76662445068359375不只是任何19位十进制数。它恰好是一个19位十进制数字,也可以精确地表示为双精度二进制浮点数。因此,初始转换隐含在:

1
double wtf = 36.76662445068359375000;

是准确的。wtf正好包含b100100.11000100010000011,没有发生舍入。

nsdecimalNumber的规格说明,它将数字表示为38位十进制尾数和范围为[-127128]的十进制指数,因此wtf中的值也可以准确表示为nsdecimalNumber。因此,我们可以得出结论,numberWithDouble没有提供正确的转换。虽然我找不到声称这个转换例程是正确四舍五入的文档,但没有很好的理由不这样做。这是一个真正的错误,请报告它。

我注意到iphoneos上的字符串格式化程序似乎提供了正确的四舍五入结果,因此您可以解决这一问题,首先将double格式化为具有38位精度的字符串,然后使用decimalNumberWithString。不太理想,但可能对你有用。


一般不建议使用double来尝试超过大约16位的十进制精度。坦率地说,我很惊讶您能够以一种在注销时保持这种精度的方式来表示这个double(有19个有效数字)。你甚至可能在iphone上得到不同的行为,iphone将long double类型映射为纯double类型(你的mac可能会在幕后处理这一问题)。

您看到的舍入可能发生在二进制级别(请参阅此处了解更多信息),因此您不会看到您期望的十进制舍入。

正是因为这些原因,如果您需要这种高精度的数学,那么您将希望从开始到结束完全使用nsdecimalnumbers或nsdecimals。为此,不要转换为浮点类型或从浮点类型转换,而是直接使用nsstring填充和导出数字(或将其存储为核心数据中的nsdecimalNumbers)。

例如,您可以使用以下代码来解决上述问题:

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

nsdecimalNumbers(及其C结构等效nsdecimal)最多可以处理38个有效数字。