Skip to content

Commit

Permalink
change isequal to return false for numbers of different types. fixes #…
Browse files Browse the repository at this point in the history
…3385

also updates hash() accordingly
generally, fewer and simpler definitions are needed.
  • Loading branch information
JeffBezanson committed Aug 23, 2013
1 parent 6c3596b commit fc05d74
Show file tree
Hide file tree
Showing 15 changed files with 44 additions and 50 deletions.
4 changes: 1 addition & 3 deletions base/complex.jl
Original file line number Diff line number Diff line change
Expand Up @@ -118,10 +118,8 @@ convert(::Type{Complex}, x::Real) = complex(x)
==(x::Real, z::Complex) = isreal(z) && real(z) == x

isequal(z::Complex, w::Complex) = isequal(real(z),real(w)) & isequal(imag(z),imag(w))
isequal(z::Complex, x::Real) = isreal(z) && isequal(real(z),x)
isequal(x::Real, z::Complex) = isreal(z) && isequal(real(z),x)

hash(z::Complex) = (r = hash(real(z)); isreal(z) ? r : bitmix(r,hash(imag(z))))
hash(z::Complex) = bitmix(hash(real(z)),hash(imag(z)))

conj(z::Complex) = complex(real(z),-imag(z))
abs(z::Complex) = hypot(real(z), imag(z))
Expand Down
26 changes: 8 additions & 18 deletions base/dict.jl
Original file line number Diff line number Diff line change
Expand Up @@ -218,28 +218,13 @@ function hash(x::Integer)
return h
end

@eval function hash(x::FloatingPoint)
if trunc(x) == x
# hash as integer if equal to some integer. note the result of
# float to int conversion is only defined for in-range values.
if x < 0
if $(float64(typemin(Int64))) <= x
return hash(int64(x))
end
else
# note: float64(typemax(Uint64)) == 2^64
if x < $(float64(typemax(Uint64)))
return hash(uint64(x))
end
end
end
isnan(x) ? $(hash64(NaN)) : hash64(float64(x))
end
hash(x::Float32) = hash(reinterpret(Uint32, isnan(x) ? NaN32 : x))
hash(x::Float64) = hash(reinterpret(Uint64, isnan(x) ? NaN : x))

function hash(t::Tuple)
h = int(0)
for i=1:length(t)
h = bitmix(h,int(hash(t[i])))
h = bitmix(h,int(hash(t[i]))+42)

This comment has been minimized.

Copy link
@StefanKarpinski

StefanKarpinski Aug 24, 2013

Member

Is the 42 to prevent a collision between 1 and (1,)?

end
return uint(h)
end
Expand All @@ -252,6 +237,9 @@ function hash(a::Array)
return uint(h)
end

# make sure Array{Bool} and BitArray can be equivalent
hash(a::Array{Bool}) = hash(bitpack(a))

hash(x::ANY) = object_id(x)

if WORD_SIZE == 64
Expand All @@ -268,6 +256,8 @@ else
s.data, length(s.data), uint32(seed))
end

hash(s::String) = hash(bytestring(s))


# dict

Expand Down
4 changes: 1 addition & 3 deletions base/float.jl
Original file line number Diff line number Diff line change
Expand Up @@ -157,16 +157,14 @@ mod{T<:FloatingPoint}(x::T, y::T) = rem(y+rem(x,y),y)
<=(x::Float32, y::Float32) = le_float(unbox(Float32,x),unbox(Float32,y))
<=(x::Float64, y::Float64) = le_float(unbox(Float64,x),unbox(Float64,y))

isequal(x::FloatingPoint, y::FloatingPoint) =
isequal{T<:FloatingPoint}(x::T, y::T) =
((x==y) & (signbit(x)==signbit(y))) | (isnan(x)&isnan(y))

isequal(x::Float32, y::Float32) = fpiseq(unbox(Float32,x),unbox(Float32,y))
isequal(x::Float64, y::Float64) = fpiseq(unbox(Float64,x),unbox(Float64,y))
isless (x::Float32, y::Float32) = fpislt(unbox(Float32,x),unbox(Float32,y))
isless (x::Float64, y::Float64) = fpislt(unbox(Float64,x),unbox(Float64,y))

isequal(a::Integer, b::FloatingPoint) = (a==b) & isequal(float(a),b)
isequal(a::FloatingPoint, b::Integer) = isequal(b, a)
isless (a::Integer, b::FloatingPoint) = (a<b) | isless(float(a),b)
isless (a::FloatingPoint, b::Integer) = (a<b) | isless(a,float(b))

Expand Down
2 changes: 2 additions & 0 deletions base/float16.jl
Original file line number Diff line number Diff line change
Expand Up @@ -101,3 +101,5 @@ end

<(x::Float16, y::Float16) = float32(x) < float32(y)
isless(x::Float16, y::Float16) = isless(float32(x), float32(y))

hash(x::Float16) = hash(reinterpret(Uint16, isnan(x) ? NaN16 : x))

This comment has been minimized.

Copy link
@StefanKarpinski

StefanKarpinski Aug 24, 2013

Member

This might be a collision problem:

julia> reinterpret(Uint16,float16(0.0))
0x0000

Of course, the same issue exists for Float32 and Float64.

19 changes: 18 additions & 1 deletion base/mpfr.jl
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import
gamma, lgamma, digamma, erf, erfc, zeta, log1p, airyai, iceil, ifloor,
itrunc, eps, signbit, sin, cos, tan, sec, csc, cot, acos, asin, atan,
cosh, sinh, tanh, sech, csch, coth, acosh, asinh, atanh, atan2,
serialize, deserialize, inf, nan
serialize, deserialize, inf, nan, hash

import Base.Math.lgamma_r

Expand Down Expand Up @@ -717,4 +717,21 @@ print(io::IO, b::BigFloat) = print(io, string(b))
show(io::IO, b::BigFloat) = print(io, string(b), " with $(precision(b)) bits of precision")
showcompact(io::IO, b::BigFloat) = print(io, string(b))

function hash(x::BigFloat)
if isnan(x)
return hash(NaN)
end
if isinf(x)
return hash(float64(x))
end
n = ceil(precision(x)/53)
h::Uint = signbit(x)
for i=1:n
f64 = float64(x)
h = bitmix(h, hash(f64)$11111)
x -= f64
end
h
end

end #module
5 changes: 2 additions & 3 deletions base/operators.jl
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,8 @@ isequal(x,y) = is(x,y)
.> (x,y) = y.<x
.>=(x,y) = y.<=x

# these definitions allow Number types to implement
# == and < instead of isequal and isless, which is more idiomatic:
isequal(x::Number, y::Number) = x==y
# this definition allows Number types to implement < instead of isless,
# which is more idiomatic:
isless(x::Real, y::Real) = x<y

max(x,y) = y < x ? x : y
Expand Down
3 changes: 0 additions & 3 deletions base/range.jl
Original file line number Diff line number Diff line change
Expand Up @@ -150,9 +150,6 @@ done(r::Ranges, i) = (length(r) <= i)

==(r::Ranges, s::Ranges) = (r.start==s.start) & (step(r)==step(s)) & (r.len==s.len)
==(r::Range1, s::Range1) = (r.start==s.start) & (r.len==s.len)
isequal(r::Ranges, s::Ranges) =
isequal(r.start,s.start) & isequal(step(r),step(s)) & (r.len==s.len)
isequal(r::Range1, s::Range1) = isequal(r.start,s.start) & (r.len==s.len)

# TODO: isless?

Expand Down
4 changes: 1 addition & 3 deletions base/rational.jl
Original file line number Diff line number Diff line change
Expand Up @@ -105,9 +105,7 @@ typemax{T<:Integer}(::Type{Rational{T}}) = one(T)//zero(T)
isinteger(x::Rational) = x.den == 1
isfloat64(x::Rational) = ispow2(x.den) & (abs(x.num) <= x.den*maxintfloat(Float64))

hash(x::Rational) = isinteger(x) ? hash(x.num) :
isfloat64(x) ? hash(float64(x)) :
bitmix(hash(x.num), hash(x.den))
hash(x::Rational) = bitmix(hash(x.num), ~hash(x.den))

-(x::Rational) = (-x.num) // x.den
for op in (:+, :-, :rem, :mod)
Expand Down
2 changes: 0 additions & 2 deletions base/string.jl
Original file line number Diff line number Diff line change
Expand Up @@ -286,8 +286,6 @@ end
isequal(a::String, b::String) = cmp(a,b) == 0
isless(a::String, b::String) = cmp(a,b) < 0

hash(s::String) = hash(bytestring(s))

# begins with and ends with predicates

function beginswith(a::String, b::String)
Expand Down
3 changes: 2 additions & 1 deletion doc/stdlib/base.rst
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,8 @@ All Objects
.. function:: isequal(x, y)

True if and only if ``x`` and ``y`` have the same contents. Loosely speaking, this means ``x`` and ``y`` would look the same when printed. This is the default comparison function used by hash tables (``Dict``).
New types with a notion of equality should implement this function, except for numbers, which should implement ``==`` instead. However, numeric types with special values might need to implement ``isequal`` as well. For example, floating point ``NaN`` values are not ``==``, but are all equivalent in the sense of ``isequal``.
New types with a notion of equality should implement this function, except for numbers, which should implement ``==`` instead. However, numeric types with special values might need to implement ``isequal`` as well. For example, floating point ``NaN`` values are not ``==``, but are all equivalent in the sense of ``isequal``. Numbers of different types are considered unequal.
Mutable containers should generally implement ``isequal`` by calling ``isequal`` recursively on all contents.

.. function:: isless(x, y)

Expand Down
4 changes: 2 additions & 2 deletions test/arrayops.jl
Original file line number Diff line number Diff line change
Expand Up @@ -200,8 +200,8 @@ v = pop!(l)
@test length(l)==2

# concatenation
@test isequal([ones(2,2) 2*ones(2,1)], [1 1 2; 1 1 2])
@test isequal([ones(2,2), 2*ones(1,2)], [1 1; 1 1; 2 2])
@test isequal([ones(2,2) 2*ones(2,1)], [1. 1 2; 1 1 2])
@test isequal([ones(2,2), 2*ones(1,2)], [1. 1; 1 1; 2 2])

# typed array literals
X = Float64[1 2 3]
Expand Down
2 changes: 1 addition & 1 deletion test/bitarray.jl
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ s4 = 4

b1 = randbool(n1, n2)
@test isequal(bitpack(bitunpack(b1)), b1)
i1 = rand(1:2, n1, n2) - 1
i1 = rand(false:true, n1, n2)
@test isequal(bitunpack(bitpack(i1)), i1)

timesofar("conversions")
Expand Down
4 changes: 0 additions & 4 deletions test/hashing.jl
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,4 @@ for T=types, S=types, x=vals
# end
end

f = prevfloat(float64(typemax(Uint64)))
@test hash(f) == hash(0xfffffffffffff800)
@test hash(f) == hash(-2048)

@test hash(RopeString("1","2")) == hash("12")
4 changes: 2 additions & 2 deletions test/mpfr.jl
Original file line number Diff line number Diff line change
Expand Up @@ -377,7 +377,7 @@ with_bigfloat_precision(53) do
end
end
for f in (:sin,:cos,:tan,:sec,:csc,:cot,:cosh,:sinh,:tanh,
:sech,:csch,:coth,:acosh,:asinh),
:sech,:csch,:coth,:acosh,:asinh),
j in (1., 1.5, 1.9)
@eval begin
@test_approx_eq ($f)(BigFloat($j)) ($f)($j)
Expand All @@ -393,7 +393,7 @@ end

# atan2
with_bigfloat_precision(53) do
@test isequal(atan2(12,2), atan2(BigFloat(12), BigFloat(2)))
@test atan2(12,2) == atan2(BigFloat(12), BigFloat(2))
end

# ldexp
Expand Down
8 changes: 4 additions & 4 deletions test/numbers.jl
Original file line number Diff line number Diff line change
Expand Up @@ -346,8 +346,8 @@
@test !isequal(+1.0,-1.0)
@test !isequal(+Inf,-Inf)

@test isequal(-0.0f0,-0.0)
@test isequal( 0.0f0, 0.0)
@test !isequal(-0.0f0,-0.0)
@test !isequal( 0.0f0, 0.0)
@test !isequal(-0.0f0, 0.0)
@test !isequal(0.0f0 ,-0.0)

Expand Down Expand Up @@ -423,8 +423,8 @@
@test !isless(+NaN,-NaN)
@test !isless(+NaN,+NaN)

@test isequal( 0, 0.0)
@test isequal( 0.0, 0)
@test !isequal( 0, 0.0)
@test !isequal( 0.0, 0)
@test !isequal( 0,-0.0)
@test !isequal(-0.0, 0)
@test isless(-0.0, 0)
Expand Down

0 comments on commit fc05d74

Please sign in to comment.