Swift - 对于 X == -X,Decimal 的 hashValue 是相同的,不能用于比较 hashValues

标签 swift decimal hashable

我们发现,如果一个是另一个的负数,则无法通过 hashValue 区分两个小数。我们使用 Decimals 作为结构中的字段,并且该结构实现了 Hashable 以便能够放入集合中。然后,我们的业务逻辑要求所有字段都是唯一的,因此将所有字段组合起来作为 hashValue。这意味着两个结构体的小数字段是另一个结构体的负数,并且其余字段实际上相等,那么整个结构体被认为是相等的。这不是我们想要的。

Playground 代码:

for i in 0..<10 {
    let randomNumber: Int = Int.random(in: 0..<10000000)

    let lhs = Decimal(integerLiteral: randomNumber)
    let rhs = Decimal(integerLiteral: -randomNumber)

    print("Are \(lhs) and \(rhs)'s hashValues equal? \(lhs.hashValue == rhs.hashValue)")
    print("Are \(randomNumber) and \(-randomNumber)'s hashValues equal? \(randomNumber.hashValue == (-randomNumber).hashValue)\n")
}

使用 doubleLiteral 而不是 integerLiteral 进行测试时也会发生同样的情况。

解决方法是直接比较小数,如果其他部分需要,也可以选择将其包含在 hashValue 中。

这种行为是故意的吗?尾数是相同的,所以我猜它们不被认为相等的原因是因为符号不包含在 Decimal 的 hashValue 中?

最佳答案

相同的对象必须具有相同的哈希值,但反之则不然:不同的对象可以具有相同的哈希值。相等性测试必须使用 == 来完成,并且永远不要单独依赖哈希值。

在这种特殊情况下,请注意有超过 264 Decimal 值,因此实际上不可能分配不同的哈希值对他们所有人的值(value)观。 (对于字符串、数组、字典等也类似)。

如果您有一个包含 Decimal (可能还有其他)属性的自定义结构,那么 EquatableHashable 协议(protocol)的实现应该如下所示这个:

struct Foo: Hashable {

    let value: Decimal
    let otherValue: Int

    static func == (lhs: Foo, rhs: Foo) -> Bool {
        return lhs.value == rhs.value && lhs.otherValue == rhs.otherValue
    }

    func hash(into hasher: inout Hasher) {
        hasher.combine(value)
        hasher.combine(otherValue)
    }
}

请注意,如果所有存储的属性都是可哈希,那么编译器可以自动合成这些方法,并且声明一致性就足够了:

struct Foo: Hashable {
    let value: Decimal
    let otherValue: Int
}

备注:我假设该行为是从 Foundation 类型 NSDecimalNumber 继承的。在 Xcode 11 beta (Swift 5.1) 中,x-xDecimal 具有不同的哈希值,但与 NSDecimalNumber 具有相同的哈希值:

let d1: Decimal = 123
let d2: Decimal = -123

print(d1.hashValue) // 1891002061093723710
print(d2.hashValue) // -6669334682005615919

print(NSDecimalNumber(decimal: d1).hashValue) // 326495598603
print(NSDecimalNumber(decimal: d2).hashValue) // 326495598603

(由于哈希值从 Swift 4.2 开始是随机的,因此您的值可能会有所不同。)但上述内容仍然适用:总是可能发生冲突,并且不能依赖具有不同哈希值的不同值。

关于Swift - 对于 X == -X,Decimal 的 hashValue 是相同的,不能用于比较 hashValues,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56597530/

相关文章:

ios - 我如何调用其他类的函数? swift ,Xcode

json - 如何使用 SwiftyJSON 解析带有 AnyHashable 值的 PayPal JSON 响应?

python - python 中是否有任何可哈希的内置对象是可变的?

arrays - 编码自定义对象的 nsarray 时无法使用 Hashable

Swift:如何使用类名声明属性

ios - 为什么让 viewController 解雇自己是不好的做法?

c - 012e2 是八进制还是 float ?

c++ - 在使用小数的简单 C++ 程序中遇到问题。

javascript 加法会产生奇怪的小数问题

ios - 在 AVPlayer 中循环远程获取视频是否会导致重新下载?