可可 - 我发现了我认为是NSDecimalNumber的错误

时间:2010-03-19 16:57:00

标签: iphone cocoa iphone-sdk-3.0

这是一个简单的代码,显示我在处理双数字时的错误...

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

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

答案是36.76662445070000018177 ??????

myDoubleAgain现在有更多数字!!!!

2 个答案:

答案 0 :(得分:10)

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

这个问题比我们通常看到的要有趣得多,它说明了“浮动点是不精确的,读取的”每个计算机科学家都应该知道的人群智慧的错误...... 'lolz“。

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

double wtf = 36.76662445068359375000;

确切。 wtf完全包含b100100.11000100010000011,并且未发生舍入。

NSDecimalNumber的规范表示它将数字表示为38位十进制尾数和范围为[-127,128]的十进制指数,因此wtf中的值也可以完全表示为NSDecimalNumber。因此,我们可能会认为numberWithDouble没有提供正确的转化。虽然我找不到声称这个转换例程被正确舍入的文档,但没有充分的理由不这样做。 这是一个真正的错误please report it

我注意到iPhoneOS上的字符串格式化程序似乎提供了正确的舍入结果,因此您可以通过首先将double格式化为38位精度的字符串然后使用decimalNumberWithString来解决此问题。不理想,但它可能适合你。

答案 1 :(得分:2)

使用double尝试超过大约16位小数精度通常是not recommended。坦率地说,我很惊讶您能够以一种在记录时保持该精度的方式来表示此double(带有19位有效数字)。您甚至可能会在iPhone本身上获得不同的行为,它将long double类型映射到普通double(您的Mac可能会在幕后处理这个问题)。

您看到的舍入可能发生在二进制级别(see here for more on this),因此您将看不到您期望的小数舍入。

出于这些原因,如果您需要这种高精度数学运算,您将希望从头到尾完全使用NSDecimalNumbers或NSDecimals。要做到这一点,不要转换为浮点类型,而是直接使用NSStrings来填充和导出数字(或将它们存储为核心数据中的NSDecimalNumbers)。

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

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

NSDecimalNumbers(及其C结构等效NSDecimal)最多可处理38位有效数字。