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’这一群体智慧的错误所在。
1 | double wtf = 36.76662445068359375000; |
是准确的。
nsdecimalNumber的规格说明,它将数字表示为38位十进制尾数和范围为[-127128]的十进制指数,因此
我注意到iphoneos上的字符串格式化程序似乎提供了正确的四舍五入结果,因此您可以解决这一问题,首先将double格式化为具有38位精度的字符串,然后使用
一般不建议使用
您看到的舍入可能发生在二进制级别(请参阅此处了解更多信息),因此您不会看到您期望的十进制舍入。
正是因为这些原因,如果您需要这种高精度的数学,那么您将希望从开始到结束完全使用nsdecimalnumbers或nsdecimals。为此,不要转换为浮点类型或从浮点类型转换,而是直接使用nsstring填充和导出数字(或将其存储为核心数据中的nsdecimalNumbers)。
例如,您可以使用以下代码来解决上述问题:
1 | id xxx = [NSDecimalNumber decimalNumberWithString:@"36.76662445068359375000"]; |
nsdecimalNumbers(及其C结构等效nsdecimal)最多可以处理38个有效数字。