【问题标题】:Inconsistency converting decimal to double (Objective C)将十进制转换为双精度的不一致(目标 C)
【发布时间】:2018-08-30 14:01:46
【问题描述】:

XCode 9.4.1。在调试器控制台中,我看到对我来说似乎很奇怪的结果:

(lldb) print (double)0.07
(double) $0 = 0.070000000000000007
(lldb) print [(NSDecimalNumber*)[NSDecimalNumber decimalNumberWithString:@"0.07"] doubleValue]
(double) $1 = 0.069999999999999993

如果在编译代码中执行,我看到的结果相同。我不明白为什么将文字 0.07 转换为双精度以及将十进制 0.07 转换为双精度时结果不同。为什么精度损失不同?

我错过了什么?

【问题讨论】:

  • 在纸上你有无限的精度,但在计算机计算中它的精度是有限的。去研究数值方法。
  • 您只有来自不同来源的 double 值,它们自己解释 0.7 数字。第一种情况是 LLVM,第二种情况是 NSDecimalNumber 的实现。就这样。我认为这没什么大不了的。
  • 为什么要使用NSDecimalNumber 以及为什么要将NSDecimalNumber 转换为double?

标签: objective-c nsdecimalnumber


【解决方案1】:

值的计算方式不同

(lldb) p 7.0 / 100.0
(double) $0 = 0.070000000000000007
(lldb) p 7.0 / 10.0 / 10.0
(double) $1 = 0.069999999999999993

【讨论】:

  • 您有任何参考资料来解释这一点吗?为什么要这样计算?
  • 我没有任何参考资料,这是有根据的猜测。编译器正在解析一个字符串,NSDecimalNumber 正在转换一个十进制数。试试po [NSNumber numberWithDouble:0.07],效果差不多。
  • 好吧,NSDecimalNumber 也得到一个字符串作为输入。编译器将字符串直接转换为双精度,而NSDecimalNumber 首先将其转换为十进制格式。但是在字符串表示和十进制格式之间不应该有任何精度损失。所以,我只是好奇为什么结果不同。
【解决方案2】:

NSDecimalNumber 的行为与您所看到的完全一样。它执行“以 10 为基数的数学”以避免您所看到的问题——数字的传统浮点表示不能准确地表示我们习惯于编写的以 10 为基数的数字。

指示您“学习数值方法”的评论有点鲁莽,但它的方向是正确的。更好地利用您的时间是(也)查看NSDecimalNumber 的文档并思考它的作用以及它存在的原因。

几分钟前我从未听说过NSDecimalNumber,所以感谢您向我指出一些新知识。 :-)

【讨论】:

  • 可能我不够清楚。问题是为什么在一种情况下我看到 0.070000000000000007 值,而在另一种情况下,值是 0.069999999999999993。它们都从相同的十进制值产生。似乎编译器和 NSDecimalNumber 类的转换方式不同。
  • 可能舍入不同
  • 在从 base-10 表示到 base-2 表示的转换中,有时可能会出现足够的剩余浮点精度损失,从而以这种方式导致表示的差异。这无济于事,并导致意外的字符串表示差异,如bugs.swift.org/browse/SR-7054
  • 你说得很清楚。您收到的 cmets 和答案直接解决了您的问题。当您将 0.7 转换为二进制以将其存储为双精度(或将其转换为双精度)时,结果是 0.0001000111101 加上一堆二进制数字。如果您从该值转换回以 10 为底的值,则不会得到精确的 0.07。你得到 1/16+1/256+1/512+1/1024+1/2048 大约是 0.06982421875。当您将二进制分数扩展到更多位数时,它将更接近 0.07,但我认为它永远不会到达那里......
  • ...另一方面,如果您将“.07”放入NSDecimalNumber(它的创建目的正是为了解决您正在观察的问题 ) 它不存储为二进制分数,而是存储为包含所有有效数字(在本例中为 7)和指数(在本例中为 -2)的大整数。由于我们存储的是 7 而不是 0.07,因此不会损失精度,因为 7 可以很容易地用二进制 (111) 表示。您是正确的,“转换方式不同”。究竟如何/为什么这样做不同是我们一直试图解释的。 :-)
猜你喜欢
  • 1970-01-01
  • 2011-07-31
  • 1970-01-01
  • 1970-01-01
  • 2011-04-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多