Skip to content

Commit

Permalink
Merge pull request #31 from crichez/floatingpoint-zero-hash-fix
Browse files Browse the repository at this point in the history
Fix Floating Point Zero Hash Values
  • Loading branch information
crichez committed Feb 4, 2022
2 parents e7d8156 + 3425741 commit 0c8ce0c
Show file tree
Hide file tree
Showing 2 changed files with 82 additions and 3 deletions.
6 changes: 3 additions & 3 deletions Sources/FowlerNollVo/FNVHashable.swift
Original file line number Diff line number Diff line change
Expand Up @@ -48,21 +48,21 @@ public protocol FNVHashable {

extension Double: FNVHashable {
public func hash<Hasher>(into hasher: inout Hasher) where Hasher : FNVHasher {
withUnsafeBytes(of: self) { hasher.combine($0) }
hasher.combine(isZero ? Double.zero.bitPattern : bitPattern)
}
}

extension Float: FNVHashable {
public func hash<Hasher>(into hasher: inout Hasher) where Hasher : FNVHasher {
withUnsafeBytes(of: self) { hasher.combine($0) }
hasher.combine(isZero ? Float.zero.bitPattern : bitPattern)
}
}

#if swift(>=5.4) && !((os(macOS) || targetEnvironment(macCatalyst)) && arch(x86_64))
@available(macOS 11, iOS 14, watchOS 7, tvOS 14, *)
extension Float16: FNVHashable {
public func hash<Hasher>(into hasher: inout Hasher) where Hasher : FNVHasher {
withUnsafeBytes(of: self) { hasher.combine($0) }
hasher.combine(isZero ? Float16.zero.bitPattern : bitPattern)
}
}
#endif
Expand Down
79 changes: 79 additions & 0 deletions Tests/FowlerNollVoTests/FloatingPointHashingTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
//
// FloatingPointHashingTests.swift
//
//
// Created by Christopher Richez on 2/3/22.
//

import FowlerNollVo
import XCTest

/// This test case asserts different representations of zero still return the same hash value.
class FloatingPointHashingTests: XCTestCase {
func test<T, Hasher>(type: T.Type, hasher: Hasher.Type)
where T : BinaryFloatingPoint & FNVHashable, Hasher : FNVHasher, Hasher.Digest : Equatable {
// Get both representations of zero
let plusZero = T(sign: .plus, exponent: 1, significand: 0.0)
let minusZero = T(sign: .minus, exponent: 1, significand: 0.0)

// Hash both values
var plusHasher = Hasher()
plusZero.hash(into: &plusHasher)
var minusHasher = Hasher()
minusZero.hash(into: &minusHasher)

// Compare their digests
XCTAssertEqual(plusHasher.digest, minusHasher.digest)
}

func testFloat() {
let type = Float.self
test(type: type, hasher: FNV32.self)
test(type: type, hasher: FNV32a.self)
test(type: type, hasher: FNV64.self)
test(type: type, hasher: FNV64a.self)
test(type: type, hasher: FNV128.self)
test(type: type, hasher: FNV128a.self)
test(type: type, hasher: FNV256.self)
test(type: type, hasher: FNV256a.self)
test(type: type, hasher: FNV512.self)
test(type: type, hasher: FNV512a.self)
test(type: type, hasher: FNV1024.self)
test(type: type, hasher: FNV1024a.self)
}

func testDouble() {
let type = Double.self
test(type: type, hasher: FNV32.self)
test(type: type, hasher: FNV32a.self)
test(type: type, hasher: FNV64.self)
test(type: type, hasher: FNV64a.self)
test(type: type, hasher: FNV128.self)
test(type: type, hasher: FNV128a.self)
test(type: type, hasher: FNV256.self)
test(type: type, hasher: FNV256a.self)
test(type: type, hasher: FNV512.self)
test(type: type, hasher: FNV512a.self)
test(type: type, hasher: FNV1024.self)
test(type: type, hasher: FNV1024a.self)
}

#if swift(>=5.4) && !((os(macOS) || targetEnvironment(macCatalyst)) && arch(x86_64))
@available(macOS 11, iOS 14, tvOS 14, watchOS 7, *)
func testFloat16() {
let type = Float16.self
test(type: type, hasher: FNV32.self)
test(type: type, hasher: FNV32a.self)
test(type: type, hasher: FNV64.self)
test(type: type, hasher: FNV64a.self)
test(type: type, hasher: FNV128.self)
test(type: type, hasher: FNV128a.self)
test(type: type, hasher: FNV256.self)
test(type: type, hasher: FNV256a.self)
test(type: type, hasher: FNV512.self)
test(type: type, hasher: FNV512a.self)
test(type: type, hasher: FNV1024.self)
test(type: type, hasher: FNV1024a.self)
}
#endif
}

0 comments on commit 0c8ce0c

Please sign in to comment.