-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
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
Changes from 1 commit
effff39
1d5050b
8495944
e1690fc
c67837c
06241fe
ec607da
50f1d10
16c2330
9f0ef0d
b3e30f9
3336fbc
4d1c138
000f41e
8c39ad2
8321a9e
53b6dee
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
…able arithmetic
- Loading branch information
There are no files selected for viewing
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()) | ||
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) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. so why was this here? There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 | ||
|
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)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 | ||
|
||
|
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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)?
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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