本文介绍了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源代码可以看到
,它们最终调用:




  • 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 inthe comments – a binary floating point number cannot represent thenumber 8.7 precisely. Swift uses the IEEE 754 standard for representingsingle- 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 excellentQ&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 thanthe 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 resultto the standard output.
  • Otherwise, if an argument conforms to CustomDebugStringConvertible,the print function calls is debugDescription property and printsthe result to the standard output.
  • Otherwise, some other mechanism is used. (Not imported here for ourpurpose.)

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 notconform to CustomStringConvertible, but toCustomDebugStringConvertible. Therefore the print function callsthe debugDescription property of Optional, which in turncalls the debugDescription of the underlying Double.Therefore – apart from being an optional – the number output isthe same as in

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

But what is the difference between description and debugDescriptionfor floating point values? From the Swift source code one can seethat both ultimately call the swift_floatingPointToStringfunction 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. Thatstring can be converted to a Double and back to a string givingthe same result.debugDescription creates a string with more decimal digits, so thatany two different floating point values will produce a different output.


Summary:

  • Most decimal numbers cannot be represented exactly as a binaryfloating point value.
  • The description and debugDescription methods of the floatingpoint types use a different precision for the conversion to astring. 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 optionalbefore printing it:

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

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

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

09-05 19:13
查看更多