swift:将字符串转换为double的问题

v-i*_*a-l 8 cocoa cocoa-touch ios swift

这是Xcode 7.3.1 playground中的一个简单代码:

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

输出结果令人惊讶: Optional(8.6999999999999993)

还,Float(str)给出:8.69999981

对这个家伙的任何想法或理由?任何对此的参考将不胜感激.

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

编辑

在swift中:

(str as NSString).doubleValue返回8.7

现在,那没关系.但我的问题仍然没有得到完整答案.我们找到了另一种选择,但为什么我们不能依赖Double("8.7"​​).请更深入地了解这一点.

编辑2

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

所以,问题又开始了.

Mar*_*n R 13

这里有两个不同的问题.首先 - 正如评论中已经提到的 - 二进制浮点数不能8.7精确地表示数字.Swift使用IEEE 754标准来表示单精度和双精度浮点数,如果指定的话

let x = 8.7
Run Code Online (Sandbox Code Playgroud)

然后存储最接近的可表示数字x,即

8.699999999999999289457264239899814128875732421875
Run Code Online (Sandbox Code Playgroud)

关于这一点的更多信息可以在优秀的Q&A Is浮点数学中找到?.


第二个问题是:为什么数字有时打印为"8.7"​​,有时打印为"8.6999999999999993"?

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

let x = 8.7
print(x) // 8.7
Run Code Online (Sandbox Code Playgroud)

有什么Double("8.7")不同8.7?比另一个更准确吗?

要回答这些问题,我们需要知道print() 函数的工作原理:

  • 如果参数符合CustomStringConvertible,则print函数调用其description属性并将结果输出到标准输出.
  • 否则,如果参数符合CustomDebugStringConvertible,则print函数调用是debugDescriptionproperty并将结果打印到标准输出.
  • 否则,使用一些其他机制.(此处不是为了我们的目的而导入的.)

Double类型符合CustomStringConvertible,因此

let x = 8.7
print(x) // 8.7
Run Code Online (Sandbox Code Playgroud)

产生相同的输出

let x = 8.7
print(x.description) // 8.7
Run Code Online (Sandbox Code Playgroud)

但是会发生什么

let str = "8.7"
print(Double(str)) // Optional(8.6999999999999993)
Run Code Online (Sandbox Code Playgroud)

Double(str)可选的,并struct Optional不会 符合CustomStringConvertible,而是要 CustomDebugStringConvertible.因此,print函数调用debugDescription属性Optional,而属性又调用debugDescription底层属性Double.因此 - 除了可选之外 - 数字输出与in相同

let x = 8.7
print(x.debugDescription) // 8.6999999999999993
Run Code Online (Sandbox Code Playgroud)

但 浮点值descriptiondebugDescription浮点值之间有什么区别?从夫特源代码的一个可以看到,无论最终调用swift_floatingPointToString 函数中Stubs.cpp,与Debug参数设置为falsetrue,分别.这可以控制数字到字符串转换的精度:

  int Precision = std::numeric_limits<T>::digits10;
  if (Debug) {
    Precision = std::numeric_limits<T>::max_digits10;
  }
Run Code Online (Sandbox Code Playgroud)

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

  • digits10 - 可以无变化地表示的小数位数,
  • max_digits10 - 区分此类型的所有值所需的小数位数.

因此description创建一个十进制数较少的字符串.该字符串可以转换为a Double并返回到字符串,从而得到相同的结果. debugDescription创建一个具有更多十进制数字的字符串,以便任何两个不同的浮点值将产生不同的输出.


摘要:

  • 大多数十进制数不能完全表示为二进制浮点值.
  • 浮点类型的descriptiondebugDescription方法使用不同的精度来转换为字符串.作为结果,
  • 打印可选的浮点值使用不同的精度进行转换,而不是打印非可选值.

因此,在您的情况下,您可能希望在打印之前打开可选项:

let str = "8.7"
if let d = Double(str) {
    print(d) // 8.7
}
Run Code Online (Sandbox Code Playgroud)

为了更好地控制,使用NSNumberFormatter或格式化打印%.<precision>f格式.

另一种选择可以是(NS)DecimalNumber代替Double (例如货币金额),参见例如swift中的Round Issue.

  • @vishal:`("6.9" as NSString).doubleValue` 在我的测试中打印了 `"6.9"`。但即使没有:除非您使用数字格式化程序,否则您不能依赖某个输出。 (2认同)