Skip to content

Commit

Permalink
fix hashing regression. (#50655)
Browse files Browse the repository at this point in the history
This fixes 2 bugs introduced by #49996 and #50041.
Closes #50628.

(cherry picked from commit c777c71)
  • Loading branch information
oscardssmith authored and KristofferC committed Aug 10, 2023
1 parent c33aad8 commit 3e133c9
Show file tree
Hide file tree
Showing 3 changed files with 26 additions and 8 deletions.
16 changes: 9 additions & 7 deletions base/float.jl
Original file line number Diff line number Diff line change
Expand Up @@ -688,22 +688,24 @@ function hash(x::Real, h::UInt)
den_z = trailing_zeros(den)
den >>= den_z
pow += num_z - den_z

# handle values representable as Int64, UInt64, Float64
# If the real can be represented as an Int64, UInt64, or Float64, hash as those types.
# To be an Integer the denominator must be 1 and the power must be non-negative.
if den == 1
# left = ceil(log2(num*2^pow))
left = top_set_bit(abs(num)) + pow
right = pow + den_z
if -1074 <= right
if 0 <= right
# 2^-1074 is the minimum Float64 so if the power is smaller, not a Float64
if -1074 <= pow
if 0 <= pow # if pow is non-negative, it is an integer
left <= 63 && return hash(Int64(num) << Int(pow), h)
left <= 64 && !signbit(num) && return hash(UInt64(num) << Int(pow), h)
end # typemin(Int64) handled by Float64 case
left <= 1024 && left - right <= 53 && return hash(ldexp(Float64(num), pow), h)
# 2^1024 is the maximum Float64 so if the power is greater, not a Float64
# Float64s only have 53 mantisa bits (including implicit bit)
left <= 1024 && left - pow <= 53 && return hash(ldexp(Float64(num), pow), h)
end
else
h = hash_integer(den, h)
end

# handle generic rational values
h = hash_integer(pow, h)
h = hash_integer(num, h)
Expand Down
3 changes: 2 additions & 1 deletion base/rational.jl
Original file line number Diff line number Diff line change
Expand Up @@ -549,9 +549,10 @@ function hash(x::Rational{<:BitInteger64}, h::UInt)
num, den = Base.numerator(x), Base.denominator(x)
den == 1 && return hash(num, h)
den == 0 && return hash(ifelse(num > 0, Inf, -Inf), h)
if isodd(den)
if isodd(den) # since den != 1, this rational can't be a Float64
pow = trailing_zeros(num)
num >>= pow
h = hash_integer(den, h)
else
pow = trailing_zeros(den)
den >>= pow
Expand Down
15 changes: 15 additions & 0 deletions test/hashing.jl
Original file line number Diff line number Diff line change
Expand Up @@ -310,3 +310,18 @@ struct AUnionParam{T<:Union{Nothing,Float32,Float64}} end
@test Type{AUnionParam{<:Union{Nothing,Float32,Float64}}} === Type{AUnionParam}
@test Type{AUnionParam.body}.hash == 0
@test Type{Base.Broadcast.Broadcasted}.hash != 0


@testset "issue 50628" begin
# test hashing of rationals that equal floats are equal to the float hash
@test hash(5//2) == hash(big(5)//2) == hash(2.5)
# test hashing of rational that are integers hash to the integer
@test hash(Int64(5)^25) == hash(big(5)^25) == hash(Int64(5)^25//1) == hash(big(5)^25//1)
# test integer/rational that don't fit in Float64 don't hash as Float64
@test hash(Int64(5)^25) != hash(5.0^25)
@test hash((Int64(5)//2)^25) == hash(big(5//2)^25)
# test integer/rational that don't fit in Float64 don't hash as Float64
@test hash((Int64(5)//2)^25) != hash(2.5^25)
# test hashing of rational with odd denominator
@test hash(5//3) == hash(big(5)//3)
end

0 comments on commit 3e133c9

Please sign in to comment.