swift:在将字符串转换为double时发生错误 [英] swift: issue in converting string to double

查看:823
本文介绍了swift:在将字符串转换为double时发生错误的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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



var str =8.7​​
print (double(str))



输出令人惊讶:
可选(8.6999999999999993)

也可以, Float(str) 8.69999981



这家伙的任何想法或理由?

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

swift中的

编辑





(str as NSString).doubleValue returns 8.7



现在,确定。但我的问题,仍然,没有得到一个完整的答案。我们找到了一个替代,但为什么我们不能依赖Double(8.7​​)。



()。

6.9as NSString).doubleValue // prints 6.9000000000000004



所以,问题再次打开。

解决方案

这里有两个不同的问题。首先 - 如
中提到的注释 - 二进制浮点数不能精确地表示
数字 8.7 。 Swift使用IEEE 754标准表示
单精度和双精度浮点数,如果您指定

  let x = 8.7 

那么最近的可表示数存储在 code>,即

  8.699999999999999289457264239899814128875732421875 

有关这方面的更多信息可以在优秀的
Q& A 是浮点数学破裂吗?






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

  let str = 8.7
print(Double(str))//可选(8.6999999999999993)

let x = 8.7
print(x)// 8.7

Double(8.7​​)不同8.7 ?是否比
更精确?



要回答这些问题,我们需要知道 print()
function works:




  • 如果参数符合 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))//可选(8.6999999999999993)

(str)可选 struct可选不会
符合 CustomStringConvertible ,而是
CustomDebugStringConvertible 。因此,打印函数调用
可选 debugDescription 属性,其中
调用底层 Double debugDescription
因此,除了是可选的之外,数字输出是
,与

中的相同

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

描述 debugDescription
的浮点值?从Swift源代码可以看到
,它们最终调用 swift_floatingPointToString

函数参数集的 Debug 中的stubs.cpp/stubs.cpp =nofollow>分别到 false true
这控制数字到字符串转换的精度:

  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 创建一个具有更多小数位数的字符串,因此
任何两个不同的浮点值将产生不同的输出。






摘要:




  • 大多数十进制数不能精确表示为二元
    浮点值。

  • 描述 debugDescription 浮点
    点类型的方法使用不同的精度转换为
    字符串。因此,与打印非可选值相比,

  • 打印可选浮点值使用的转换精度不同。



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

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

为了更好的控制,请使用 NSNumberFormatter %。< precision> f 格式。


Here is a simple code in Xcode 7.3.1 playground:

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

the output is suprising: Optional(8.6999999999999993)

also, Float(str) gives: 8.69999981

Any thoughts or reasons on this guys? Any references to this would be appreciated.

Also, how should I then convert "8.7" to 8.7 as Double (or Float)?

Edit

in swift:

(str as NSString).doubleValue returns 8.7

Now, that is Ok. But my question, still, does not get a complete answer. We have found an alternative but why can we not rely on Double("8.7"). Please, give a deeper insight on this.

Edit 2

("6.9" as NSString).doubleValue // prints 6.9000000000000004

So, the question opens up again.

解决方案

There are two different issues here. First – as already mentioned in the comments – a binary floating point number cannot represent the number 8.7 precisely. Swift uses the IEEE 754 standard for representing single- and double-precision floating point numbers, and if you assign

let x = 8.7

then the closest representable number is stored in x, and that is

8.699999999999999289457264239899814128875732421875

Much more information about this can be found in the excellent Q&A Is floating point math broken?.


The second issue is: Why is the number sometimes printed as "8.7" and sometimes as "8.6999999999999993"?

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

let x = 8.7
print(x) // 8.7

Is Double("8.7") different from 8.7? Is one more precise than the other?

To answer these questions, we need to know how the print() function works:

  • If an argument conforms to CustomStringConvertible, the print function calls its description property and prints the result to the standard output.
  • Otherwise, if an argument conforms to CustomDebugStringConvertible, the print function calls is debugDescription property and prints the result to the standard output.
  • Otherwise, some other mechanism is used. (Not imported here for our purpose.)

The Double type conforms to CustomStringConvertible, therefore

let x = 8.7
print(x) // 8.7

produces the same output as

let x = 8.7
print(x.description) // 8.7

But what happens in

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

Double(str) is an optional, and struct Optional does not conform to CustomStringConvertible, but to CustomDebugStringConvertible. Therefore the print function calls the debugDescription property of Optional, which in turn calls the debugDescription of the underlying Double. Therefore – apart from being an optional – the number output is the same as in

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

But what is the difference between description and debugDescription for floating point values? From the Swift source code one can see that both ultimately call the swift_floatingPointToString function in Stubs.cpp, with the Debug parameter set to false and true, respectively. This controls the precision of the number to string conversion:

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

For the meaning of those constants, see http://en.cppreference.com/w/cpp/types/numeric_limits:

  • digits10 – number of decimal digits that can be represented without change,
  • max_digits10 – number of decimal digits necessary to differentiate all values of this type.

So description creates a string with less decimal digits. That string can be converted to a Double and back to a string giving the same result. debugDescription creates a string with more decimal digits, so that any two different floating point values will produce a different output.


Summary:

  • Most decimal numbers cannot be represented exactly as a binary floating point value.
  • The description and debugDescription methods of the floating point types use a different precision for the conversion to a string. As a consequence,
  • printing an optional floating point value uses a different precision for the conversion than printing a non-optional value.

Therefore in your case, you probably want to unwrap the optional before printing it:

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

For better control, use NSNumberFormatter or formatted printing with the %.<precision>f format.

这篇关于swift:在将字符串转换为double时发生错误的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆