【问题标题】:Why are doubles printed differently in dictionaries?为什么双打在字典中的打印方式不同?
【发布时间】:2017-04-18 23:03:23
【问题描述】:
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

【问题讨论】:

  • 看起来并不完全像一个错误,但它有点不一致
  • FWIW,1.1的精确值和2.3是1.100000000000000088817841970012523233890533447265625和2.29999999999999982236431605997495353221893310546875,解释该偏差的方向和大小跨度>
  • @harold 你是什么意思他们的确切价值是那个。我不跟。您是从哪里/如何得出这些数字的?
  • 这是浮点数的本质。了解更多详情in this answer
  • 另外,你可能不应该使用双精度字典。

标签: swift


【解决方案1】:

正如 cmets 中已经提到的,Double 不能存储 值1.1 完全一致。 Swift 使用(像许多其他语言一样) 根据IEEE 754 的二进制浮点数 标准。

可以表示为Double 的最接近1.1 的数字是

1.100000000000000088817841970012523233890533447265625

最接近2.3 且可以表示为Double 的数字是

2.29999999999999982236431605997495353221893310546875

打印那个数字意味着它被转换成一个字符串 又是十进制表示,这是用不同的方法完成的 精度,取决于您打印数字的方式。

HashedCollections.swift.gyb的源代码可以看出description的方法 DictionarydebugPrint() 用于键和值, debugPrint(x) 打印 x.debugDescription 的值 (如果x 符合CustomDebugStringConvertible)。

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

所以你看到的是description 的不同输出 和debugDescriptionDouble

print(1.1.description) // 1.1
print(1.1.debugDescription) // 1.1000000000000001

从 Swift 源代码可以看到 两者都使用swift_floatingPointToString() Stubs.cpp 中的函数,Debug 参数分别设置为 falsetrue。 该参数控制数字到字符串转换的精度:

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

有关这些常量的含义,请参阅std::numeric_limits

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

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

【讨论】:

  • 有趣。所以我只是尝试了let dic : [Double : Double] = [1.1 : 2.3, 1.1000000000000001 : 2.3],我得到了错误 fatal error: Dictionary literal contains duplicate keys ...而不出所料let dic : [Double : Double] = [1.1 : 2.3, 1.10000000000000031 : 2.3] 不会产生错误。也不确定我是否能一口气完全理解这一点。时间会有所帮助:)
  • @Honey:是的,当存储在 Double 中时,两个值都会转换为相同的 64 位二进制浮点数。试试let x = 1.1; let y = 1.1000000000000001; print(x == y)
  • 所以你的意思是每个双精度数总是以 16 位小数精度格式表示?
  • @Honey:对不起,我不明白你的意思。 FloatDouble 使用 IEEE 754 标准,参见例如en.wikipedia.org/wiki/IEEE_floating_point。这个帖子很有启发性:stackoverflow.com/questions/588004/….
  • 我的意思是.后面有16位数字,即它们以这种格式表示
【解决方案2】:

是的,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 将被视为字符串而不是实际的浮点数

【讨论】:

    猜你喜欢
    • 2017-12-13
    • 1970-01-01
    • 2023-01-10
    • 2017-05-08
    • 1970-01-01
    • 2012-11-16
    • 2021-09-22
    • 1970-01-01
    • 2022-01-13
    相关资源
    最近更新 更多