Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: checked integer conversions #8420

Merged
merged 17 commits into from
Sep 29, 2014
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
effff39
check integer truncation (#5413) and make more operators follow T+T =…
JeffBezanson Sep 17, 2014
1d5050b
get numbers test passing with checked integer conversions and type-st…
JeffBezanson Sep 17, 2014
8495944
make a couple things safe for checked signed<->unsigned conversion
JeffBezanson Sep 17, 2014
e1690fc
check signed<->unsigned conversions
JeffBezanson Sep 18, 2014
c67837c
get dates tests passing with checked signed<->unsigned conversion
JeffBezanson Sep 19, 2014
06241fe
Merge branch 'master' of github.com:JuliaLang/julia into jb/checked_i…
JeffBezanson Sep 19, 2014
ec607da
get a few more things working with checked integer conversions
JeffBezanson Sep 19, 2014
50f1d10
Merge branch 'master' of github.com:JuliaLang/julia into jb/checked_i…
JeffBezanson Sep 25, 2014
16c2330
make signed() and unsigned() unchecked. check only in convert()
JeffBezanson Sep 25, 2014
9f0ef0d
add a bit of widening to reductions, to make up for the new
JeffBezanson Sep 25, 2014
b3e30f9
Merge branch 'master' of github.com:JuliaLang/julia into jb/checked_i…
JeffBezanson Sep 26, 2014
3336fbc
fix more cases of widening in reductions
JeffBezanson Sep 26, 2014
4d1c138
fix more issues with integer conversion in random number generation
JeffBezanson Sep 26, 2014
000f41e
Merge branch 'master' of github.com:JuliaLang/julia into jb/checked_i…
JeffBezanson Sep 26, 2014
8c39ad2
Merge branch 'master' of github.com:JuliaLang/julia into jb/checked_i…
JeffBezanson Sep 26, 2014
8321a9e
doc and test updates for checked integer conversions
JeffBezanson Sep 28, 2014
53b6dee
Merge branch 'master' of github.com:JuliaLang/julia into jb/checked_i…
JeffBezanson Sep 28, 2014
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
make signed() and unsigned() unchecked. check only in convert()
a few fixes in base for the convert and arithmetic changes

get all tests passing
  • Loading branch information
JeffBezanson committed Sep 25, 2014
commit 16c2330da55ed9b9eb84353a292db495aad562b7
4 changes: 2 additions & 2 deletions base/char.jl
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ convert(::Type{Char}, x::Float64) = char(convert(Uint32, x))
## char promotions ##

promote_rule(::Type{Char}, ::Type{Int8}) = Int32
promote_rule(::Type{Char}, ::Type{Uint8}) = Int32
promote_rule(::Type{Char}, ::Type{Uint8}) = Uint32
promote_rule(::Type{Char}, ::Type{Int16}) = Int32
promote_rule(::Type{Char}, ::Type{Uint16}) = Int32
promote_rule(::Type{Char}, ::Type{Uint16}) = Uint32
promote_rule(::Type{Char}, ::Type{Int32}) = Int32
promote_rule(::Type{Char}, ::Type{Uint32}) = Uint32
promote_rule(::Type{Char}, ::Type{Int64}) = Int64
Expand Down
2 changes: 1 addition & 1 deletion base/datafmt.jl
Original file line number Diff line number Diff line change
Expand Up @@ -338,7 +338,7 @@ function dlm_parse{T,D}(dbuff::T, eol::D, dlm::D, qchar::D, cchar::D, ign_adj_dl
all_ascii = (D <: Uint8) || (isascii(eol) && isascii(dlm) && (!allow_quote || isascii(qchar)) && (!allow_comments || isascii(cchar)))
(T <: UTF8String) && all_ascii && (return dlm_parse(dbuff.data, uint8(eol), uint8(dlm), uint8(qchar), uint8(cchar), ign_adj_dlm, allow_quote, allow_comments, skipstart, skipblanks, dh))
ncols = nrows = col = 0
is_default_dlm = (dlm == convert(D, invalid_dlm))
is_default_dlm = (dlm == itrunc(D, invalid_dlm))
error_str = ""
# 0: begin field, 1: quoted field, 2: unquoted field, 3: second quote (could either be end of field or escape character), 4: comment, 5: skipstart
state = (skipstart > 0) ? 5 : 0
Expand Down
6 changes: 3 additions & 3 deletions base/grisu/fastfixed.jl
Original file line number Diff line number Diff line change
Expand Up @@ -110,8 +110,8 @@ function fillfractionals(fractionals, exponent,
return len, decimal_point
end

low(x) = uint64(x)
high(x) = uint64(x >> 64)
low(x) = uint64(x&0xffffffffffffffff)
high(x) = uint64(x >>> 64)
bitat(x::Uint128,y) = y >= 64 ? (int32(high(x) >> (y-64)) & 1) : (int32(low(x) >> y) & 1)
function divrem2(x,power)
h = high(x)
Expand Down Expand Up @@ -219,4 +219,4 @@ function fastfixedtoa(v,mode,fractional_count,buffer)
decimal_point = -fractional_count
end
return true, len, decimal_point, buffer
end
end
48 changes: 25 additions & 23 deletions base/int.jl
Original file line number Diff line number Diff line change
Expand Up @@ -43,23 +43,23 @@ abs(x::Signed) = flipsign(x,x)

~(n::Integer) = -n-1

asunsigned(x::Integer) = reinterpret(typeof(unsigned(zero(x))), x)
asunsigned(x::Bool) = unsigned(x)
asunsigned(x) = unsigned(x)
assigned(x::Integer) = reinterpret(typeof(signed(zero(x))), x)
assigned(x) = signed(x)
unsigned(x::Signed) = reinterpret(typeof(convert(Unsigned,zero(x))), x)
unsigned(x::Bool) = convert(Unsigned, x)
unsigned(x) = convert(Unsigned, x)
signed(x::Unsigned) = reinterpret(typeof(convert(Signed,zero(x))), x)
signed(x) = convert(Signed, x)

div(x::Signed, y::Unsigned) = flipsign(assigned(div(asunsigned(abs(x)),y)),x)
div(x::Unsigned, y::Signed) = asunsigned(flipsign(assigned(div(x,asunsigned(abs(y)))),y))
div(x::Signed, y::Unsigned) = flipsign(signed(div(unsigned(abs(x)),y)),x)
div(x::Unsigned, y::Signed) = unsigned(flipsign(signed(div(x,unsigned(abs(y)))),y))

rem(x::Signed, y::Unsigned) = flipsign(assigned(rem(asunsigned(abs(x)),y)),x)
rem(x::Unsigned, y::Signed) = rem(x,asunsigned(abs(y)))
rem(x::Signed, y::Unsigned) = flipsign(signed(rem(unsigned(abs(x)),y)),x)
rem(x::Unsigned, y::Signed) = rem(x,unsigned(abs(y)))

fld(x::Signed, y::Unsigned) = div(x,y)-(signbit(x)&(rem(x,y)!=0))
fld(x::Unsigned, y::Signed) = div(x,y)-(signbit(y)&(rem(x,y)!=0))

mod(x::Signed, y::Unsigned) = rem(y+asunsigned(rem(x,y)),y)
mod(x::Unsigned, y::Signed) = rem(y+assigned(rem(x,y)),y)
mod(x::Signed, y::Unsigned) = rem(y+unsigned(rem(x,y)),y)
mod(x::Unsigned, y::Signed) = rem(y+signed(rem(x,y)),y)

cld(x::Signed, y::Unsigned) = div(x,y)+(!signbit(x)&(rem(x,y)!=0))
cld(x::Unsigned, y::Signed) = div(x,y)+(!signbit(y)&(rem(x,y)!=0))
Expand Down Expand Up @@ -138,12 +138,12 @@ for T in IntTypes
end
end

==(x::Signed, y::Unsigned) = (x >= 0) & (asunsigned(x) == y)
==(x::Unsigned, y::Signed ) = (y >= 0) & (x == asunsigned(y))
< (x::Signed, y::Unsigned) = (x < 0) | (asunsigned(x) < y)
< (x::Unsigned, y::Signed ) = (y > 0) & (x < asunsigned(y))
<=(x::Signed, y::Unsigned) = (x <= 0) | (asunsigned(x) <= y)
<=(x::Unsigned, y::Signed ) = (y >= 0) & (x <= asunsigned(y))
==(x::Signed, y::Unsigned) = (x >= 0) & (unsigned(x) == y)
==(x::Unsigned, y::Signed ) = (y >= 0) & (x == unsigned(y))
< (x::Signed, y::Unsigned) = (x < 0) | (unsigned(x) < y)
< (x::Unsigned, y::Signed ) = (y > 0) & (x < unsigned(y))
<=(x::Signed, y::Unsigned) = (x <= 0) | (unsigned(x) <= y)
<=(x::Unsigned, y::Signed ) = (y >= 0) & (x <= unsigned(y))

## integer conversions ##

Expand All @@ -165,12 +165,16 @@ for to in tuple(IntTypes...,Char), from in tuple(IntTypes...,Char,Bool)
elseif !(issubtype(from,Signed) === issubtype(to,Signed))
# raise InexactError if x's top bit is set
@eval convert(::Type{$to}, x::($from)) = box($to,check_top_bit(unbox($from,x)))
@eval itrunc(::Type{$to}, x::($from)) = box($to,unbox($from,x))
else
@eval convert(::Type{$to}, x::($from)) = box($to,unbox($from,x))
end
end
end

itrunc{T<:Integer}(::Type{T}, x::T) = x
itrunc(::Type{Bool}, x::Integer) = ((x&1)!=0)

for to in (Int8, Int16, Int32, Int64)
@eval begin
convert(::Type{$to}, x::Float32) = box($to,checked_fptosi($to,unbox(Float32,x)))
Expand Down Expand Up @@ -240,8 +244,6 @@ uint32(x) = convert(Uint32,x)
uint64(x) = convert(Uint64,x)
uint128(x) = convert(Uint128,x)

signed(x) = convert(Signed,x)
unsigned(x) = convert(Unsigned,x)
integer(x) = convert(Integer,x)

round(x::Integer) = x
Expand Down Expand Up @@ -356,12 +358,12 @@ typemax(::Type{Uint64}) = 0xffffffffffffffff
@eval typemin(::Type{Int128} ) = $(convert(Int128,1)<<int32(127))
@eval typemax(::Type{Int128} ) = $(box(Int128,unbox(Uint128,typemax(Uint128)>>int32(1))))
Copy link
Sponsor Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why are these @eval? are you just concerned this creates extra work for the inliner / llvm? it seems the resulting code should be unconditionally the same, after llvm's constant propagation pass

Copy link
Sponsor Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're probably right but yes, I don't want to tax our inlining heuristics with this.


widen(::Type{Int8}) = Int16
widen(::Type{Int16}) = Int32
widen(::Type{Int8}) = Int
widen(::Type{Int16}) = Int
widen(::Type{Int32}) = Int64
widen(::Type{Int64}) = Int128
widen(::Type{Uint8}) = Uint16
widen(::Type{Uint16}) = Uint32
widen(::Type{Uint8}) = Uint
widen(::Type{Uint16}) = Uint
widen(::Type{Uint32}) = Uint64
widen(::Type{Uint64}) = Uint128

Expand Down
5 changes: 2 additions & 3 deletions base/random.jl
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ for (T, U) in [(Uint8, Uint32), (Uint16, Uint32),
(Int8, Uint32), (Int16, Uint32), (Int32, Uint32), (Int64, Uint64), (Int128, Uint128),
(Bool, Uint32), (Char, Uint32)]

@eval RandIntGen(r::UnitRange{$T}) = isempty(r) ? error("range must be non-empty") : RandIntGen(first(r), convert($U, last(r) - first(r) + 1)) # overflow ok
@eval RandIntGen(r::UnitRange{$T}) = isempty(r) ? error("range must be non-empty") : RandIntGen(first(r), convert($U, unsigned(last(r) - first(r) + one($T)))) # overflow ok
end

# this function uses 32 bit entropy for small ranges of length <= typemax(Uint32) + 1
Expand All @@ -205,8 +205,7 @@ function rand{T<:Integer, U<:Unsigned}(g::RandIntGen{T,U})
while x > g.u
x = rand(U)
end
# TODO: fix for when T is smaller than U
reinterpret(T, Base.asunsigned(g.a) + rem_knuth(x, g.k))
itrunc(T, g.a + rem_knuth(x, g.k))
end

rand{T<:Union(Signed,Unsigned,Bool,Char)}(r::UnitRange{T}) = rand(RandIntGen(r))
Expand Down
4 changes: 2 additions & 2 deletions base/range.jl
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,9 @@ immutable StepRange{T,S} <: OrdinalRange{T,S}
if T<:Signed && (diff > zero(diff)) != (stop > start)
# handle overflowed subtraction with unsigned rem
if diff > zero(diff)
remain = -oftype(T, asunsigned(-diff) % step)
remain = -oftype(T, unsigned(-diff) % step)
else
remain = oftype(T, asunsigned(diff) % step)
remain = oftype(T, unsigned(diff) % step)
end
else
remain = steprem(start,stop,step)
Expand Down
4 changes: 2 additions & 2 deletions base/utf16.jl
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ end
utf16_is_lead(c::Uint16) = (c & 0xfc00) == 0xd800
utf16_is_trail(c::Uint16) = (c & 0xfc00) == 0xdc00
utf16_is_surrogate(c::Uint16) = (c & 0xf800) == 0xd800
utf16_get_supplementary(lead::Uint16, trail::Uint16) = char((lead-0xd7f7)<<10 + trail)
utf16_get_supplementary(lead::Uint16, trail::Uint16) = char(uint32(lead-0xd7f7)<<10 + trail)

function endof(s::UTF16String)
d = s.data
Expand Down Expand Up @@ -91,7 +91,7 @@ convert(T::Type{UTF16String}, data::AbstractArray{Int16}) =
function convert(T::Type{UTF16String}, bytes::AbstractArray{Uint8})
isempty(bytes) && return UTF16String(Uint16[0])
isodd(length(bytes)) && throw(ArgumentError("odd number of bytes"))
data = reinterpret(Uint16, bytes)
data = reinterpret(Uint16, bytes)
# check for byte-order mark (BOM):
if data[1] == 0xfeff # native byte order
d = Array(Uint16, length(data))
Expand Down
94 changes: 45 additions & 49 deletions test/numbers.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1133,21 +1133,19 @@ end
@test cld(typemin(Int64)+3,-2) == 4611686018427387903
@test cld(typemin(Int64)+3,-7) == 1317624576693539401

import Base.asunsigned

for x={typemin(Int64), -typemax(Int64), -typemax(Int64)+1, -typemax(Int64)+2,
typemax(Int64)-2, typemax(Int64)-1, typemax(Int64),
typemax(Uint64)-1, typemax(Uint64)-2, typemax(Uint64)},
y={-7,-2,-1,1,2,7}
if x >= 0
@test div(asunsigned(x),y) == asunsigned(div(x,y))
@test fld(asunsigned(x),y) == asunsigned(fld(x,y))
@test cld(asunsigned(x),y) == asunsigned(cld(x,y))
@test div(unsigned(x),y) == unsigned(div(x,y))
@test fld(unsigned(x),y) == unsigned(fld(x,y))
@test cld(unsigned(x),y) == unsigned(cld(x,y))
end
if isa(x,Signed) && y >= 0
@test div(x,asunsigned(y)) == div(x,y)
@test fld(x,asunsigned(y)) == fld(x,y)
@test cld(x,asunsigned(y)) == cld(x,y)
@test div(x,unsigned(y)) == div(x,y)
@test fld(x,unsigned(y)) == fld(x,y)
@test cld(x,unsigned(y)) == cld(x,y)
end
end

Expand Down Expand Up @@ -1190,22 +1188,20 @@ end
@test div(typemax(Uint64)-2, 1) == typemax(Uint64)-2
@test div(typemax(Uint64)-2,-1) == -typemax(Uint64)+2

using Base.assigned

@test assigned(div(asunsigned(typemax(Int64))+2, 1)) == typemax(Int64)+2
@test assigned(div(asunsigned(typemax(Int64))+2,-1)) == -typemax(Int64)-2
@test assigned(div(asunsigned(typemax(Int64))+1, 1)) == typemax(Int64)+1
@test assigned(div(asunsigned(typemax(Int64))+1,-1)) == -typemax(Int64)-1
@test assigned(div(asunsigned(typemax(Int64)) , 1)) == typemax(Int64)
@test assigned(div(asunsigned(typemax(Int64)) ,-1)) == -typemax(Int64)

@test assigned(div(typemax(Uint),typemax(Int))) == 2
@test assigned(div(typemax(Uint),(typemax(Int)>>1)+1)) == 3
@test assigned(div(typemax(Uint),typemax(Int)>>1)) == 4
@test assigned(div(typemax(Uint),typemin(Int))) == -1
@test assigned(div(typemax(Uint),typemin(Int)+1)) == -2
@test assigned(div(typemax(Uint),typemin(Int)>>1)) == -3
@test assigned(div(typemax(Uint),(typemin(Int)>>1)+1)) == -4
@test signed(div(unsigned(typemax(Int64))+2, 1)) == typemax(Int64)+2
@test signed(div(unsigned(typemax(Int64))+2,-1)) == -typemax(Int64)-2
@test signed(div(unsigned(typemax(Int64))+1, 1)) == typemax(Int64)+1
@test signed(div(unsigned(typemax(Int64))+1,-1)) == -typemax(Int64)-1
@test signed(div(unsigned(typemax(Int64)) , 1)) == typemax(Int64)
@test signed(div(unsigned(typemax(Int64)) ,-1)) == -typemax(Int64)

@test signed(div(typemax(Uint),typemax(Int))) == 2
@test signed(div(typemax(Uint),(typemax(Int)>>1)+1)) == 3
@test signed(div(typemax(Uint),typemax(Int)>>1)) == 4
@test signed(div(typemax(Uint),typemin(Int))) == -1
@test signed(div(typemax(Uint),typemin(Int)+1)) == -2
@test signed(div(typemax(Uint),typemin(Int)>>1)) == -3
@test signed(div(typemax(Uint),(typemin(Int)>>1)+1)) == -4

@test fld(typemax(Uint64) , 1) == typemax(Uint64)
@test fld(typemax(Uint64) ,-1) == -typemax(Uint64)
Expand All @@ -1214,20 +1210,20 @@ using Base.assigned
@test fld(typemax(Uint64)-2, 1) == typemax(Uint64)-2
@test fld(typemax(Uint64)-2,-1) == -typemax(Uint64)+2

@test assigned(fld(asunsigned(typemax(Int64))+2, 1)) == typemax(Int64)+2
@test assigned(fld(asunsigned(typemax(Int64))+2,-1)) == -typemax(Int64)-2
@test assigned(fld(asunsigned(typemax(Int64))+1, 1)) == typemax(Int64)+1
@test assigned(fld(asunsigned(typemax(Int64))+1,-1)) == -typemax(Int64)-1
@test assigned(fld(asunsigned(typemax(Int64)) , 1)) == typemax(Int64)
@test assigned(fld(asunsigned(typemax(Int64)) ,-1)) == -typemax(Int64)

@test assigned(fld(typemax(Uint),typemax(Int))) == 2
@test assigned(fld(typemax(Uint),(typemax(Int)>>1)+1)) == 3
@test assigned(fld(typemax(Uint),typemax(Int)>>1)) == 4
@test assigned(fld(typemax(Uint),typemin(Int))) == -2
@test assigned(fld(typemax(Uint),typemin(Int)+1)) == -3
@test assigned(fld(typemax(Uint),typemin(Int)>>1)) == -4
@test assigned(fld(typemax(Uint),(typemin(Int)>>1)+1)) == -5
@test signed(fld(unsigned(typemax(Int64))+2, 1)) == typemax(Int64)+2
@test signed(fld(unsigned(typemax(Int64))+2,-1)) == -typemax(Int64)-2
@test signed(fld(unsigned(typemax(Int64))+1, 1)) == typemax(Int64)+1
@test signed(fld(unsigned(typemax(Int64))+1,-1)) == -typemax(Int64)-1
@test signed(fld(unsigned(typemax(Int64)) , 1)) == typemax(Int64)
@test signed(fld(unsigned(typemax(Int64)) ,-1)) == -typemax(Int64)

@test signed(fld(typemax(Uint),typemax(Int))) == 2
@test signed(fld(typemax(Uint),(typemax(Int)>>1)+1)) == 3
@test signed(fld(typemax(Uint),typemax(Int)>>1)) == 4
@test signed(fld(typemax(Uint),typemin(Int))) == -2
@test signed(fld(typemax(Uint),typemin(Int)+1)) == -3
@test signed(fld(typemax(Uint),typemin(Int)>>1)) == -4
@test signed(fld(typemax(Uint),(typemin(Int)>>1)+1)) == -5

@test cld(typemax(Uint64) , 1) == typemax(Uint64)
@test cld(typemax(Uint64) ,-1) == -typemax(Uint64)
Expand All @@ -1236,20 +1232,20 @@ using Base.assigned
@test cld(typemax(Uint64)-2, 1) == typemax(Uint64)-2
@test cld(typemax(Uint64)-2,-1) == -typemax(Uint64)+2

@test assigned(cld(asunsigned(typemax(Int64))+2, 1)) == typemax(Int64)+2
@test assigned(cld(asunsigned(typemax(Int64))+2,-1)) == -typemax(Int64)-2
@test assigned(cld(asunsigned(typemax(Int64))+1, 1)) == typemax(Int64)+1
@test assigned(cld(asunsigned(typemax(Int64))+1,-1)) == -typemax(Int64)-1
@test assigned(cld(asunsigned(typemax(Int64)) , 1)) == typemax(Int64)
@test assigned(cld(asunsigned(typemax(Int64)) ,-1)) == -typemax(Int64)
@test signed(cld(unsigned(typemax(Int64))+2, 1)) == typemax(Int64)+2
@test signed(cld(unsigned(typemax(Int64))+2,-1)) == -typemax(Int64)-2
@test signed(cld(unsigned(typemax(Int64))+1, 1)) == typemax(Int64)+1
@test signed(cld(unsigned(typemax(Int64))+1,-1)) == -typemax(Int64)-1
@test signed(cld(unsigned(typemax(Int64)) , 1)) == typemax(Int64)
@test signed(cld(unsigned(typemax(Int64)) ,-1)) == -typemax(Int64)

@test signed(cld(typemax(Uint),typemax(Int))) == 3
@test signed(cld(typemax(Uint),(typemax(Int)>>1)+1)) == 4
@test signed(cld(typemax(Uint),typemax(Int)>>1)) == 5
@test assigned(cld(typemax(Uint),typemin(Int))) == -1
@test assigned(cld(typemax(Uint),typemin(Int)+1)) == -2
@test assigned(cld(typemax(Uint),typemin(Int)>>1)) == -3
@test assigned(cld(typemax(Uint),(typemin(Int)>>1)+1)) == -4
@test signed(cld(typemax(Uint),typemin(Int))) == -1
@test signed(cld(typemax(Uint),typemin(Int)+1)) == -2
@test signed(cld(typemax(Uint),typemin(Int)>>1)) == -3
@test signed(cld(typemax(Uint),(typemin(Int)>>1)+1)) == -4

# issue #4156
@test fld(1.4,0.35667494393873234) == 3.0
Expand Down Expand Up @@ -1935,7 +1931,7 @@ end
# widen
@test widen(1.5f0) === 1.5
@test widen(int32(42)) === int64(42)
@test widen(Int8) === Int16
@test widen(Int8) === Int
@test widen(Float32) === Float64
## Note: this should change to e.g. Float128 at some point
@test widen(Float64) === BigFloat
Expand Down