【问题标题】:swift: issue in converting string to doubleswift:将字符串转换为双精度的问题
【发布时间】:2017-02-07 18:42:33
【问题描述】:

这是 Xcode 7.3.1 游乐场中的一个简单代码:

var str = "8.7" print(Double(str))

输出令人惊讶: Optional(8.6999999999999993)

另外,Float(str) 给出:8.69999981

对这些人有什么想法或理由吗? 任何对此的参考将不胜感激。

另外,我应该如何将“8.7”​​转换为 8.7 作为 Double(或 Float)?

编辑

迅速:

(str as NSString).doubleValue 返回 8.7

现在,没关系。但是我的问题仍然没有得到完整的答案。我们找到了替代方案,但为什么我们不能依赖 Double("8.7")。请对此提供更深入的了解。

编辑 2

("6.9" as NSString).doubleValue // 打印 6.9000000000000004

所以,问题又来了。

【问题讨论】:

  • 使用 print(Double(str)!)
  • 如果您的问题是关于 8.7 与 8.69999981,那么您应该阅读 Is floating point math broken?What Every Computer Scientist Should Know About Floating-Point Arithmetic
  • “双精度浮点格式是一种计算机数字格式,它在计算机内存中占用 8 个字节(64 位),并通过使用浮点来表示广泛的动态值范围。”因此您必须指定值的“精度”(精度是数字中的位数)。
  • @l'L'l,Martin 但为什么将“8.7”​​转换为 Double 不会导致 8.7 而是 8.69999999999991。这样做有什么意义?如果这真的是在硬件级别上做的,那么 Swift 应该会处理它。 (就像它为 .doubleValue 将其转换为 8.7 但不是为 Double("8.7"))
  • 这个问题比我最初想象的更有趣......

标签: ios swift cocoa cocoa-touch


【解决方案1】:

您可以使用此代码可能会有用。

print(str.doubleValue)

【讨论】:

    【解决方案2】:

    我会使用:

    let doubleValue = NSNumberFormatter().numberFromString(str)?.doubleValue
    

    【讨论】:

      【解决方案3】:

      这里有两个不同的问题。首先——正如已经提到的 cmets – 二进制浮点数不能表示 号码8.7 准确。 Swift 使用 IEEE 754 标准来表示 单精度和双精度浮点数,如果你赋值

      let x = 8.7
      

      那么最接近的可表示数字存储在x中,即

      8.699999999999999289457264239899814128875732421875
      

      更多关于这方面的信息可以在优秀的 问答Is floating point math broken?.


      第二个问题是:为什么数字有时会打印为“8.7” 有时是“8.6999999999999993”?

      let str = "8.7"
      print(Double(str)) // Optional(8.6999999999999993)
      
      let x = 8.7
      print(x) // 8.7
      

      Double("8.7")8.7 不同吗?比更精确 另一个?

      要回答这些问题,我们需要知道print() 功能有效:

      • 如果参数符合CustomStringConvertible,打印函数调用其description属性并打印结果 到标准输出。
      • 否则,如果参数符合CustomDebugStringConvertible, 打印函数调用是debugDescription 属性并打印 将结果输出到标准输出。
      • 否则,将使用其他一些机制。 (这里没有为我们的 目的。)

      Double 类型符合CustomStringConvertible,因此

      let x = 8.7
      print(x) // 8.7
      

      产生与

      相同的输出
      let x = 8.7
      print(x.description) // 8.7
      

      但是发生了什么

      let str = "8.7"
      print(Double(str)) // Optional(8.6999999999999993)
      

      Double(str) 是一个可选,而struct Optional不是 符合CustomStringConvertible,但要 CustomDebugStringConvertible。因此打印函数调用 OptionaldebugDescription 属性,这反过来 调用底层DoubledebugDescription。 因此——除了是可选的——数字输出是 和

      一样
      let x = 8.7
      print(x.debugDescription) // 8.6999999999999993
      

      但是descriptiondebugDescription 有什么区别? 对于浮点值?从 Swift 源代码中可以看到 两者最终都调用swift_floatingPointToString Stubs.cpp 中的函数,Debug 参数分别设置为 falsetrue。 这控制了数字到字符串转换的精度:

        int Precision = std::numeric_limits<T>::digits10;
        if (Debug) {
          Precision = std::numeric_limits<T>::max_digits10;
        }
      

      关于这些常量的含义,请参见http://en.cppreference.com/w/cpp/types/numeric_limits

      • digits10 – 可以不加变化地表示的十进制位数,
      • max_digits10 – 区分此类型的所有值所需的小数位数。

      所以description 创建了一个小数位数较少的字符串。那 字符串可以转换为 Double 并返回给字符串 同样的结果。 debugDescription 创建一个包含更多十进制数字的字符串,这样 任何两个不同的浮点值都会产生不同的输出。


      总结:

      • 大多数十进制数不能完全表示为二进制 浮点值。
      • 浮动的descriptiondebugDescription 方法 点类型使用不同的精度转换为 细绳。因此,
      • 打印可选浮点值使用的转换精度与打印非可选值不同。

      因此,在您的情况下,您可能想要解开可选的 在打印之前:

      let str = "8.7"
      if let d = Double(str) {
          print(d) // 8.7
      }
      

      为了更好的控制,请使用NSNumberFormatter 或格式化 以%.&lt;precision&gt;f 格式打印。

      另一种选择是使用(NS)DecimalNumber 而不是Double (例如货币金额),参见例如Round Issue in swift.

      【讨论】:

      • 能否请您看一下问题中的最后一个编辑。
      • @v-i-s-h-a-l: ("6.9" as NSString).doubleValue 在我的测试中打印出"6.9"。但即使没有:您不能依赖某个输出,除非您使用数字格式化程序。
      猜你喜欢
      • 2011-07-24
      • 1970-01-01
      • 2014-07-24
      • 2018-10-02
      • 1970-01-01
      • 2011-08-11
      相关资源
      最近更新 更多