我们发现,如果两个小数中的一个是负数,就不能通过它们的哈希值来区分这两个小数。我们使用小数作为结构中的字段,并且该结构实现可以放入集的哈希。然后,我们的业务逻辑要求所有字段都是唯一的,因此所有字段都是针对哈希值组合的。这意味着两个结构,其中十进制字段是另一个的负数,其余字段实际上相等,那么整个结构就被认为是相等的。这不是我们想要的。
游乐场代码:

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进行测试时也会发生同样的情况。
解决方法是直接比较小数,如果其他部分需要,还可以选择将其包含在哈希值中。
这种行为是有意的吗?尾数是一样的,所以我猜他们不被认为相等的原因是因为这个符号不包含在十进制的散列值中?

最佳答案

相同的对象必须具有相同的哈希值,但不能反过来:不同的对象可以具有相同的哈希值。必须使用==进行相等性测试,而不要仅依赖哈希值。
在这种特殊情况下,请注意,有超过264个Decimal值,因此实际上不可能为所有这些值分配不同的哈希值。(同样适用于字符串、数组、字典等)。
如果您有一个包含Decimal属性(可能还有其他属性)的自定义结构,那么EquatableHashable协议的实现应该如下所示:

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)
    }
}

注意,如果所有存储的属性都Hashable那么编译器可以自动合成这些方法,并且声明一致性就足够了:
struct Foo: Hashable {
    let value: Decimal
    let otherValue: Int
}

注:我认为行为是从基础类型继承的。使用xcode 11 beta(swift 5.1)NSDecimalNumberx时,哈希值与-x不同,但哈希值与Decimal相同:
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 - Swift - 对于X == -X,Decimal的hashValue是相同的,不能用于比较hashValues,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/56597530/

10-17 02:17