为什么双打在字典中的打印方式不同?

时间:2016-12-04 13:43:57

标签: swift

let dic : [Double : Double] = [1.1 : 2.3, 2.3 : 1.1, 1.2 : 2.3]

print(dic)// [2.2999999999999998: 1.1000000000000001, 1.2: 2.2999999999999998, 1.1000000000000001: 2.2999999999999998]



let double : Double = 2.3
let anotherdouble : Double = 1.1

print(double) // 2.3
print(anotherdouble) // 1.1

我不明白为什么编译器以不同方式打印字典中的值? 我在Swift 3,Xcode 8上。这是一个错误或一些奇怪的方法来优化东西吗?

修改

更奇怪的是:

有些价值观会过去,有些价值会低于预期值,有些则会保持原样! 1.1小于1.1000000000000001而2.3小于2.2999999999999998,1.2仅为1.2

2 个答案:

答案 0 :(得分:14)

正如评论中已经提到的,Double无法存储 值1.1完全正确。 Swift使用(像许多其他语言一样) 根据{{​​3}}的二进制浮点数 标准。

可以表示为1.1的最接近Double的号码是

1.100000000000000088817841970012523233890533447265625

,可以表示为2.3的最接近Double的号码是

2.29999999999999982236431605997495353221893310546875

打印该数字表示将其转换为字符串 再次使用十进制表示,这是用不同的方式完成的 精度,取决于您打印数字的方式。

IEEE 754的源代码可以看到description方法 Dictionary对密钥和值使用debugPrint(), 并debugPrint(x)打印x.debugDescription的值 (如果x符合CustomDebugStringConvertible)。

另一方面,如果print(x)符合,x.description会调用xCustomStringConvertible

所以你看到的是description的不同输出 和debugDescription的{​​{1}}:

Double

从Swift源代码可以看出 两者都使用print(1.1.description) // 1.1 print(1.1.debugDescription) // 1.1000000000000001 HashedCollections.swift.gyb中的函数,swift_floatingPointToString()参数分别设置为Debugfalse。 此参数控制数字到字符串转换的精度:

true

有关这些常量的含义,请参阅Stubs.cpp

  • int Precision = std::numeric_limits<T>::digits10; if (Debug) { Precision = std::numeric_limits<T>::max_digits10; } - 无需更改即可表示的小数位数
  • digits10 - 区分此类型所有值所需的小数位数。

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

答案 1 :(得分:0)

是的,Swift在将其存储到字典

中时使用二进制浮点数

使用字典作为[Double:Any],如果您的数字是32位则使用Float然后向上转换为AnyObject

见下面的例子

    let strDecimalNumber  = "8.37"    
    var myDictionary : [String: Any] = [:] 
    myDictionary["key1"] = Float(strDecimalNumber) as AnyObject  // 8.369999999999999
    myDictionary["key2"] = Double(strDecimalNumber) as AnyObject  //8.369999999999999
    myDictionary["key3"] = Double(8.37) as AnyObject   //8.369999999999999
    myDictionary["key4"] = Float(8.37) as AnyObject  //8.37
    myDictionary["key5"] = 8.37  // 8.3699999999999992
    myDictionary["key6"] = strDecimalNumber  // "8.37" it is String
    myDictionary["key7"] = strDecimalNumber.description  // "8.37" it is String
    myDictionary["key8"] = Float(10000000.01)  // 10000000.0
    myDictionary["key9"] = Float(100000000.01) // 100000000.0
    myDictionary["key10"] = Float(1000000000.01) // 1e+09 
    myDictionary["key11"] = Double(1000000000.01) // 1000000000.01
    print(myDictionary)

myDictionary将打印为

  

[“key1”:8.37,“key2”:8.369999999999999,“key3”:8.369999999999999,   “key4”:8.37,“key5”:8.3699999999999992,“key6”:“8.37”,“key7”:“8.37”,“key8”:   10000000.0,“key9”:100000000.0,“key10”:1e + 09,“key11”:1000000000.01]

正如Martin R在上面的回答中所提到的,使用.description将被视为String而不是实际的Float