Skip to content

Commit c777c71

Browse files
authored
fix hashing regression. (#50655)
This fixes 2 bugs introduced by #49996 and #50041. Closes #50628.
2 parents 7ca0f0d + a87b164 commit c777c71

File tree

3 files changed

+26
-8
lines changed

3 files changed

+26
-8
lines changed

base/float.jl

+9-7
Original file line numberDiff line numberDiff line change
@@ -688,22 +688,24 @@ function hash(x::Real, h::UInt)
688688
den_z = trailing_zeros(den)
689689
den >>= den_z
690690
pow += num_z - den_z
691-
692-
# handle values representable as Int64, UInt64, Float64
691+
# If the real can be represented as an Int64, UInt64, or Float64, hash as those types.
692+
# To be an Integer the denominator must be 1 and the power must be non-negative.
693693
if den == 1
694+
# left = ceil(log2(num*2^pow))
694695
left = top_set_bit(abs(num)) + pow
695-
right = pow + den_z
696-
if -1074 <= right
697-
if 0 <= right
696+
# 2^-1074 is the minimum Float64 so if the power is smaller, not a Float64
697+
if -1074 <= pow
698+
if 0 <= pow # if pow is non-negative, it is an integer
698699
left <= 63 && return hash(Int64(num) << Int(pow), h)
699700
left <= 64 && !signbit(num) && return hash(UInt64(num) << Int(pow), h)
700701
end # typemin(Int64) handled by Float64 case
701-
left <= 1024 && left - right <= 53 && return hash(ldexp(Float64(num), pow), h)
702+
# 2^1024 is the maximum Float64 so if the power is greater, not a Float64
703+
# Float64s only have 53 mantisa bits (including implicit bit)
704+
left <= 1024 && left - pow <= 53 && return hash(ldexp(Float64(num), pow), h)
702705
end
703706
else
704707
h = hash_integer(den, h)
705708
end
706-
707709
# handle generic rational values
708710
h = hash_integer(pow, h)
709711
h = hash_integer(num, h)

base/rational.jl

+2-1
Original file line numberDiff line numberDiff line change
@@ -549,9 +549,10 @@ function hash(x::Rational{<:BitInteger64}, h::UInt)
549549
num, den = Base.numerator(x), Base.denominator(x)
550550
den == 1 && return hash(num, h)
551551
den == 0 && return hash(ifelse(num > 0, Inf, -Inf), h)
552-
if isodd(den)
552+
if isodd(den) # since den != 1, this rational can't be a Float64
553553
pow = trailing_zeros(num)
554554
num >>= pow
555+
h = hash_integer(den, h)
555556
else
556557
pow = trailing_zeros(den)
557558
den >>= pow

test/hashing.jl

+15
Original file line numberDiff line numberDiff line change
@@ -310,3 +310,18 @@ struct AUnionParam{T<:Union{Nothing,Float32,Float64}} end
310310
@test Type{AUnionParam{<:Union{Nothing,Float32,Float64}}} === Type{AUnionParam}
311311
@test Type{AUnionParam.body}.hash == 0
312312
@test Type{Base.Broadcast.Broadcasted}.hash != 0
313+
314+
315+
@testset "issue 50628" begin
316+
# test hashing of rationals that equal floats are equal to the float hash
317+
@test hash(5//2) == hash(big(5)//2) == hash(2.5)
318+
# test hashing of rational that are integers hash to the integer
319+
@test hash(Int64(5)^25) == hash(big(5)^25) == hash(Int64(5)^25//1) == hash(big(5)^25//1)
320+
# test integer/rational that don't fit in Float64 don't hash as Float64
321+
@test hash(Int64(5)^25) != hash(5.0^25)
322+
@test hash((Int64(5)//2)^25) == hash(big(5//2)^25)
323+
# test integer/rational that don't fit in Float64 don't hash as Float64
324+
@test hash((Int64(5)//2)^25) != hash(2.5^25)
325+
# test hashing of rational with odd denominator
326+
@test hash(5//3) == hash(big(5)//3)
327+
end

0 commit comments

Comments
 (0)