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
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
get numbers test passing with checked integer conversions and type-st…
…able arithmetic
  • Loading branch information
JeffBezanson committed Sep 17, 2014
commit 1d5050bb89566d794fc961c7207fabcbe9a8864b
4 changes: 4 additions & 0 deletions base/char.jl
Original file line number Diff line number Diff line change
@@ -3,6 +3,10 @@ char(x::FloatingPoint) = char(iround(x))

integer(x::Char) = int(x)

convert(::Type{Char}, x::Float16) = char(convert(Uint32, x))
convert(::Type{Char}, x::Float32) = char(convert(Uint32, x))
convert(::Type{Char}, x::Float64) = char(convert(Uint32, x))

## char promotions ##

promote_rule(::Type{Char}, ::Type{Int8}) = Int32
10 changes: 5 additions & 5 deletions base/float.jl
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
## conversions to floating-point ##

convert(::Type{Float32}, x::Int128) = float32(uint128(abs(x)))*(1-2(x<0))
convert(::Type{Float32}, x::Uint128) = float32(uint64(x)) + ldexp(float32(uint64(x>>>64)),64)
convert(::Type{Float32}, x::Int128) = float32(reinterpret(Uint128,abs(x)))*(1-2(x<0))
convert(::Type{Float32}, x::Uint128) = float32(uint64(x&0xffffffffffffffff)) + ldexp(float32(uint64(x>>>64)),64)
promote_rule(::Type{Float32}, ::Type{Int128} ) = Float32
promote_rule(::Type{Float32}, ::Type{Uint128}) = Float32

convert(::Type{Float64}, x::Int128) = float64(uint128(abs(x)))*(1-2(x<0))
convert(::Type{Float64}, x::Uint128) = float64(uint64(x)) + ldexp(float64(uint64(x>>>64)),64)
convert(::Type{Float64}, x::Int128) = float64(reinterpret(Uint128,abs(x)))*(1-2(x<0))
convert(::Type{Float64}, x::Uint128) = float64(uint64(x&0xffffffffffffffff)) + ldexp(float64(uint64(x>>>64)),64)
promote_rule(::Type{Float64}, ::Type{Int128} ) = Float64
promote_rule(::Type{Float64}, ::Type{Uint128}) = Float64

convert(::Type{Float16}, x::Union(Signed,Unsigned)) = convert(Float16, convert(Float32,x))
convert(::Type{Float16}, x::Integer) = convert(Float16, convert(Float32,x))
for t in (Bool,Char,Int8,Int16,Int32,Int64,Uint8,Uint16,Uint32,Uint64)
@eval promote_rule(::Type{Float16}, ::Type{$t}) = Float32
end
97 changes: 34 additions & 63 deletions base/int.jl
Original file line number Diff line number Diff line change
@@ -1,13 +1,3 @@
## type aliases ##

if Int === Int32
typealias SmallSigned Union(Int8,Int16)
typealias SmallUnsigned Union(Uint8,Uint16)
else
typealias SmallSigned Union(Int8,Int16,Int32)
typealias SmallUnsigned Union(Uint8,Uint16,Uint32)
end

## integer arithmetic ##

const IntTypes = (Int8, Uint8, Int16, Uint16, Int32, Uint32,
@@ -34,11 +24,10 @@ iseven(n::Integer) = !isodd(n)
signbit(x::Integer) = x < 0
signbit(x::Unsigned) = false

flipsign(x::Int, y::Int) = box(Int,flipsign_int(unbox(Int,x),unbox(Int,y)))
flipsign(x::Int64, y::Int64) = box(Int64,flipsign_int(unbox(Int64,x),unbox(Int64,y)))
flipsign(x::Int128, y::Int128) = box(Int128,flipsign_int(unbox(Int128,x),unbox(Int128,y)))
for T in (Int8,Int16,Int32,Int64,Int128)
@eval flipsign(x::$T, y::$T) = box($T,flipsign_int(unbox($T,x),unbox($T,y)))
end

flipsign{T<:Signed}(x::T,y::T) = flipsign(int(x),int(y))
flipsign(x::Signed, y::Signed) = flipsign(promote(x,y)...)
flipsign(x::Signed, y::Float32) = flipsign(x, reinterpret(Int32,y))
flipsign(x::Signed, y::Float64) = flipsign(x, reinterpret(Int64,y))
@@ -100,9 +89,13 @@ for T in IntTypes
($)(x::$T, y::$T) = box($T,xor_int(unbox($T,x),unbox($T,y)))

<<(x::$T, y::Int32) = box($T, shl_int(unbox($T,x),unbox(Int32,y)))
>>(x::$T, y::Int32) = box($T,ashr_int(unbox($T,x),unbox(Int32,y)))
>>>(x::$T, y::Int32) = box($T,lshr_int(unbox($T,x),unbox(Int32,y)))
end
if issubtype(T,Unsigned)
@eval >>(x::$T, y::Int32) = box($T,lshr_int(unbox($T,x),unbox(Int32,y)))
else
@eval >>(x::$T, y::Int32) = box($T,ashr_int(unbox($T,x),unbox(Int32,y)))
end
end

bswap(x::Int8) = x
@@ -129,29 +122,15 @@ trailing_ones(x::Integer) = trailing_zeros(~x)

## integer comparisons ##

<(x::Int8, y::Int8) = slt_int(unbox(Int8,x),unbox(Int8,y))
<(x::Int16, y::Int16) = slt_int(unbox(Int16,x),unbox(Int16,y))
<(x::Int32, y::Int32) = slt_int(unbox(Int32,x),unbox(Int32,y))
<(x::Int64, y::Int64) = slt_int(unbox(Int64,x),unbox(Int64,y))
<(x::Int128, y::Int128) = slt_int(unbox(Int128,x),unbox(Int128,y))

<(x::Uint8, y::Uint8) = ult_int(unbox(Uint8,x),unbox(Uint8,y))
<(x::Uint16, y::Uint16) = ult_int(unbox(Uint16,x),unbox(Uint16,y))
<(x::Uint32, y::Uint32) = ult_int(unbox(Uint32,x),unbox(Uint32,y))
<(x::Uint64, y::Uint64) = ult_int(unbox(Uint64,x),unbox(Uint64,y))
<(x::Uint128, y::Uint128) = ult_int(unbox(Uint128,x),unbox(Uint128,y))

<=(x::Int8, y::Int8) = sle_int(unbox(Int8,x),unbox(Int8,y))
<=(x::Int16, y::Int16) = sle_int(unbox(Int16,x),unbox(Int16,y))
<=(x::Int32, y::Int32) = sle_int(unbox(Int32,x),unbox(Int32,y))
<=(x::Int64, y::Int64) = sle_int(unbox(Int64,x),unbox(Int64,y))
<=(x::Int128, y::Int128) = sle_int(unbox(Int128,x),unbox(Int128,y))

<=(x::Uint8, y::Uint8) = ule_int(unbox(Uint8,x),unbox(Uint8,y))
<=(x::Uint16, y::Uint16) = ule_int(unbox(Uint16,x),unbox(Uint16,y))
<=(x::Uint32, y::Uint32) = ule_int(unbox(Uint32,x),unbox(Uint32,y))
<=(x::Uint64, y::Uint64) = ule_int(unbox(Uint64,x),unbox(Uint64,y))
<=(x::Uint128, y::Uint128) = ule_int(unbox(Uint128,x),unbox(Uint128,y))
for T in IntTypes
if issubtype(T,Signed)
@eval <( x::$T, y::$T) = slt_int(unbox($T,x),unbox($T,y))
@eval <=(x::$T, y::$T) = sle_int(unbox($T,x),unbox($T,y))
else
@eval <( x::$T, y::$T) = ult_int(unbox($T,x),unbox($T,y))
@eval <=(x::$T, y::$T) = ule_int(unbox($T,x),unbox($T,y))
end
end

==(x::Signed, y::Unsigned) = (x >= 0) & (unsigned(x) == y)
==(x::Unsigned, y::Signed ) = (y >= 0) & (x == unsigned(y))
@@ -162,11 +141,8 @@ trailing_ones(x::Integer) = trailing_zeros(~x)

## integer conversions ##

const _inttypes = (Bool, Int8, Uint8, Int16, Uint16, Int32, Uint32, Char,
Int64, Uint64, Int128, Uint128)

for to in _inttypes, from in _inttypes
if !(to === from) && !(to === Bool)
for to in tuple(IntTypes...,Char), from in tuple(IntTypes...,Char,Bool)
if !(to === from)
if to.size < from.size
if issubtype(to, Signed)
@eval convert(::Type{$to}, x::($from)) = box($to,checked_trunc_sint($to,unbox($from,x)))
@@ -218,21 +194,18 @@ function convert(::Type{Uint128}, x::FloatingPoint)
end
convert(::Type{Uint128}, x::Float32) = convert(Uint128, float64(x))

convert(::Type{Char}, x::Float32) = char(convert(Int, x))
convert(::Type{Char}, x::Float64) = char(convert(Int, x))

convert(::Type{Signed}, x::Uint8 ) = convert(Int,x)
convert(::Type{Signed}, x::Uint16 ) = convert(Int,x)
convert(::Type{Signed}, x::Uint32 ) = convert(Int,x)
convert(::Type{Signed}, x::Uint8 ) = convert(Int8,x)
convert(::Type{Signed}, x::Uint16 ) = convert(Int16,x)
convert(::Type{Signed}, x::Uint32 ) = convert(Int32,x)
convert(::Type{Signed}, x::Uint64 ) = convert(Int64,x)
convert(::Type{Signed}, x::Uint128) = convert(Int128,x)
convert(::Type{Signed}, x::Float32) = convert(Int,x)
convert(::Type{Signed}, x::Float64) = convert(Int,x)
convert(::Type{Signed}, x::Char) = convert(Int,x)

convert(::Type{Unsigned}, x::Int8 ) = convert(Uint,x)
convert(::Type{Unsigned}, x::Int16 ) = convert(Uint,x)
convert(::Type{Unsigned}, x::Int32 ) = convert(Uint,x)
convert(::Type{Unsigned}, x::Int8 ) = convert(Uint8,x)
convert(::Type{Unsigned}, x::Int16 ) = convert(Uint16,x)
convert(::Type{Unsigned}, x::Int32 ) = convert(Uint32,x)
convert(::Type{Unsigned}, x::Int64 ) = convert(Uint64,x)
convert(::Type{Unsigned}, x::Int128 ) = convert(Uint128,x)
convert(::Type{Unsigned}, x::Float32) = convert(Uint,x)
@@ -372,12 +345,12 @@ typemax(::Type{Uint64}) = 0xffffffffffffffff
typemax(::Type{Int128} ) = $(int128((uint128(-1))>>int32(1)))
end

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

@@ -506,20 +479,18 @@ end

for T in (Int8,Uint8)
@eval function checked_mul(x::$T, y::$T)
xy = x*y
xy8 = convert($T,xy)
xy == xy8 || throw(OverflowError())
return xy8
xy = widemul(x,y)
(typemin($T) <= xy <= typemax($T)) || throw(OverflowError())
Copy link
Member

Choose a reason for hiding this comment

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

it seems like this would be much faster if it utilized the llvm checked-multiply intrinsic, no?
llvm.smul.with.overflow

Copy link
Member Author

Choose a reason for hiding this comment

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

See the comment right above. LLVM's checked multiply is broken for 8-bit.

Copy link
Member

Choose a reason for hiding this comment

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

ah, now i see it – it wasn't visible in my diff (perhaps because of the intervening blank line?)

for int8, i guess this isn't really that bad

it is broken for i64 on x86 too (the below case)?

Copy link
Member

Choose a reason for hiding this comment

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

No, just i8 and i>=128: llvm-mirror/llvm@0bedfa4

Copy link
Member

Choose a reason for hiding this comment

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

so this is actually conditional on upgrading the version of julia?

Copy link
Member

Choose a reason for hiding this comment

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

using the intrinsic is, yes

return itrunc($T,xy)
end
end

if WORD_SIZE == 32
for T in (Int64,Uint64)
@eval function checked_mul(x::$T, y::$T)
xy = int128(x)*int128(y)
xy64 = convert($T,xy)
xy == xy64 || throw(OverflowError())
return xy64
(typemin($T) <= xy <= typemax($T)) || throw(OverflowError())
return itrunc($T,xy)
Copy link
Member

Choose a reason for hiding this comment

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

so why was this here?

Copy link
Member

Choose a reason for hiding this comment

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

Oh, sorry, it's everything > wordsize and this is on 32bit.

end
end
else
14 changes: 7 additions & 7 deletions base/random.jl
Original file line number Diff line number Diff line change
@@ -104,17 +104,17 @@ rand(r::MersenneTwister) = dsfmt_genrand_close_open(r.state)
dsfmt_randui32() = dsfmt_gv_genrand_uint32()
dsfmt_randui64() = uint64(dsfmt_randui32()) | (uint64(dsfmt_randui32())<<32)

rand(::Type{Uint8}) = uint8(rand(Uint32))
rand(::Type{Uint16}) = uint16(rand(Uint32))
rand(::Type{Uint8}) = itrunc(Uint8,rand(Uint32))
rand(::Type{Uint16}) = itrunc(Uint16,rand(Uint32))
rand(::Type{Uint32}) = dsfmt_randui32()
rand(::Type{Uint64}) = dsfmt_randui64()
rand(::Type{Uint128}) = uint128(rand(Uint64))<<64 | rand(Uint64)

rand(::Type{Int8}) = int8(rand(Uint8))
rand(::Type{Int16}) = int16(rand(Uint16))
rand(::Type{Int32}) = int32(rand(Uint32))
rand(::Type{Int64}) = int64(rand(Uint64))
rand(::Type{Int128}) = int128(rand(Uint128))
rand(::Type{Int8}) = itrunc(Int8,rand(Uint32))
Copy link
Member

Choose a reason for hiding this comment

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

I hadn't realized we were generating our low-bitcount random numbers this way. I presume someone knows that discarding is better than saving the other bytes somewhere for the next rand(Int8)?

Copy link
Member

Choose a reason for hiding this comment

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

i think that's where filling an array can be more efficient than asking for them one at a time

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, for filling an array it could be faster. I tried saving the extra bits for individual values once, but couldn't make it fast enough to make up for the extra bookkeeping overhead.

Copy link
Member

Choose a reason for hiding this comment

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

I'm not surprised you tried. Makes sense then to keep it like this.

rand(::Type{Int16}) = itrunc(Int16,rand(Uint32))
rand(::Type{Int32}) = reinterpret(Int32,rand(Uint32))
rand(::Type{Int64}) = reinterpret(Int64,rand(Uint64))
rand(::Type{Int128}) = reinterpret(Int128,rand(Uint128))

# Arrays of random numbers

8 changes: 8 additions & 0 deletions base/reducedim.jl
Original file line number Diff line number Diff line change
@@ -101,6 +101,14 @@ reducedim_init(f, op::OrFun, A::AbstractArray, region) = reducedim_initarray(A,

# specialize to make initialization more efficient for common cases

if Int === Int32
typealias SmallSigned Union(Int8,Int16)
typealias SmallUnsigned Union(Uint8,Uint16)
else
typealias SmallSigned Union(Int8,Int16,Int32)
typealias SmallUnsigned Union(Uint8,Uint16,Uint32)
end

typealias CommonReduceResult Union(Uint64,Uint128,Int64,Int128,Float32,Float64)

for (IT, RT) in ((CommonReduceResult, :(eltype(A))), (SmallSigned, :Int), (SmallUnsigned, :Uint))
8 changes: 4 additions & 4 deletions doc/stdlib/base.rst
Original file line number Diff line number Diff line change
@@ -3090,9 +3090,9 @@ Mathematical Functions

Returns the nearest integral value of the same type as ``x`` not greater in magnitude than ``x``. ``digits`` and ``base`` work as above.

.. function:: iround(x) -> Integer
.. function:: iround([T,]x) -> Integer

Returns the nearest integer to ``x``.
Returns the nearest integer to ``x``, converted to an integer type, optionally passed as the first argument.

.. function:: iceil(x) -> Integer

@@ -3102,9 +3102,9 @@ Mathematical Functions

Returns the nearest integer not greater than ``x``.

.. function:: itrunc(x) -> Integer
.. function:: itrunc([T,]x) -> Integer

Returns the nearest integer not greater in magnitude than ``x``.
Returns the nearest integer not greater in magnitude than ``x``, converted to an integer type, optionally passed as the first argument.

.. function:: signif(x, digits, [base])

8 changes: 4 additions & 4 deletions test/numbers.jl
Original file line number Diff line number Diff line change
@@ -1459,7 +1459,7 @@ end
@test -0b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001 ==
-(0b000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001)

@test isa(-0x00,Uint)
@test isa(-0x00,Uint8)
@test isa(-0x0000000000000000,Uint64)
@test isa(-0x00000000000000000,Uint128)
@test isa(-0x00000000000000000000000000000000,Uint128)
@@ -1901,9 +1901,9 @@ end

for T = (Uint8,Int8,Uint16,Int16,Uint32,Int32,Uint64,Int64,Uint128,Int128)
for n = 1:2:1000
@test convert(T,n*(n^typemax(T))) == one(T)
@test n*(n^typemax(T)) & typemax(T) == 1
n = rand(T) | one(T)
@test convert(T,n*(n^typemax(T))) == one(T)
@test n*(n^typemax(T)) == 1
end
end

@@ -1931,7 +1931,7 @@ end
# widen
@test widen(1.5f0) === 1.5
@test widen(int32(42)) === int64(42)
@test widen(Int8) === Int
@test widen(Int8) === Int16
@test widen(Float32) === Float64
## Note: this should change to e.g. Float128 at some point
@test widen(Float64) === BigFloat