Skip to content

Commit

Permalink
Support negative bit-shift counts for <<, >>, and >>>
Browse files Browse the repository at this point in the history
- Introduce methods that take unsigned shift counts
- Handle signed shift counts by dispatching to the respective unsigned shift counts, shifting in the opposite direction for negative counts
- Update documentation
- Add tests

Closes #14516.
  • Loading branch information
eschnett committed Mar 2, 2016
1 parent b61f660 commit e3d5fda
Show file tree
Hide file tree
Showing 7 changed files with 98 additions and 36 deletions.
10 changes: 5 additions & 5 deletions base/gmp.jl
Original file line number Diff line number Diff line change
Expand Up @@ -346,23 +346,23 @@ for (fJ, fC) in ((:-, :neg), (:~, :com))
end
end

function <<(x::BigInt, c::Int)
c < 0 && throw(DomainError())
function <<(x::BigInt, c::UInt)
c == 0 && return x
z = BigInt()
ccall((:__gmpz_mul_2exp, :libgmp), Void, (Ptr{BigInt}, Ptr{BigInt}, Culong), &z, &x, c)
return z
end

function >>(x::BigInt, c::Int)
c < 0 && throw(DomainError())
function >>(x::BigInt, c::UInt)
c == 0 && return x
z = BigInt()
ccall((:__gmpz_fdiv_q_2exp, :libgmp), Void, (Ptr{BigInt}, Ptr{BigInt}, Culong), &z, &x, c)
return z
end

>>>(x::BigInt, c::Int) = x >> c
>>>(x::BigInt, c::UInt) = x >> c



trailing_zeros(x::BigInt) = Int(ccall((:__gmpz_scan1, :libgmp), Culong, (Ptr{BigInt}, Culong), &x, 0))
trailing_ones(x::BigInt) = Int(ccall((:__gmpz_scan0, :libgmp), Culong, (Ptr{BigInt}, Culong), &x, 0))
Expand Down
32 changes: 20 additions & 12 deletions base/int.jl
Original file line number Diff line number Diff line change
Expand Up @@ -132,11 +132,6 @@ end
(|){T<:BitInteger}(x::T, y::T) = box(T, or_int(unbox(T,x),unbox(T,y)))
($){T<:BitInteger}(x::T, y::T) = box(T,xor_int(unbox(T,x),unbox(T,y)))

>>{T<:BitSigned,S<:BitInteger64}(x::T, y::S) = box(T,ashr_int(unbox(T,x),unbox(S,y)))
>>{T<:BitUnsigned,S<:BitInteger64}(x::T, y::S) = box(T,lshr_int(unbox(T,x),unbox(S,y)))
<<{T<:BitInteger,S<:BitInteger64}(x::T, y::S) = box(T, shl_int(unbox(T,x),unbox(S,y)))
>>>{T<:BitInteger,S<:BitInteger64}(x::T, y::S) = box(T,lshr_int(unbox(T,x),unbox(S,y)))

bswap{T<:Union{Int8,UInt8}}(x::T) = x
bswap{T<:Union{Int16, UInt16, Int32, UInt32, Int64, UInt64, Int128, UInt128}}(x::T) =
box(T,bswap_int(unbox(T,x)))
Expand All @@ -162,6 +157,26 @@ trailing_ones(x::Integer) = trailing_zeros(~x)
<=(x::Signed, y::Unsigned) = (x < 0) | (unsigned(x) <= y)
<=(x::Unsigned, y::Signed ) = (y >= 0) & (x <= unsigned(y))

## integer shifts ##

# unsigned shift counts always shift in the same direction
>>{T<:BitSigned,S<:BitUnsigned}(x::T, y::S) =
box(T,ashr_int(unbox(T,x),unbox(S,y)))
>>{T<:BitUnsigned,S<:BitUnsigned}(x::T, y::S) =
box(T,lshr_int(unbox(T,x),unbox(S,y)))
<<{T<:BitInteger,S<:BitUnsigned}(x::T, y::S) =
box(T, shl_int(unbox(T,x),unbox(S,y)))
>>>{T<:BitInteger,S<:BitUnsigned}(x::T, y::S) =
box(T,lshr_int(unbox(T,x),unbox(S,y)))
# signed shift counts can shift in either direction
# note: this early during bootstrap, `>=` is not yet available
>>(x::BitInteger, y::BitSigned) =
0 <= y ? x >> unsigned(y) : x << unsigned(-y)
<<(x::BitInteger, y::BitSigned) =
0 <= y ? x << unsigned(y) : x >> unsigned(-y)
>>>(x::BitInteger, y::BitSigned) =
0 <= y ? x >>> unsigned(y) : x << unsigned(-y)

## integer conversions ##

for to in BitInteger_types, from in (BitInteger_types...,Bool)
Expand Down Expand Up @@ -399,13 +414,6 @@ if WORD_SIZE == 32
function mod(x::Int128, y::Int128)
Int128(mod(BigInt(x),BigInt(y)))
end

<<( x::Int128, y::Int) = y == 0 ? x : box(Int128,shl_int(unbox(Int128,x),unbox(Int,y)))
<<( x::UInt128, y::Int) = y == 0 ? x : box(UInt128,shl_int(unbox(UInt128,x),unbox(Int,y)))
>>( x::Int128, y::Int) = y == 0 ? x : box(Int128,ashr_int(unbox(Int128,x),unbox(Int,y)))
>>( x::UInt128, y::Int) = y == 0 ? x : box(UInt128,lshr_int(unbox(UInt128,x),unbox(Int,y)))
>>>(x::Int128, y::Int) = y == 0 ? x : box(Int128,lshr_int(unbox(Int128,x),unbox(Int,y)))
>>>(x::UInt128, y::Int) = y == 0 ? x : box(UInt128,lshr_int(unbox(UInt128,x),unbox(Int,y)))
else
*{T<:Union{Int128,UInt128}}(x::T, y::T) = box(T,mul_int(unbox(T,x),unbox(T,y)))

Expand Down
2 changes: 1 addition & 1 deletion base/intfuncs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ powermod(x::Integer, p::Integer, m::Union{Int128,UInt128}) = oftype(m, powermod(
nextpow2(x::Unsigned) = one(x)<<((sizeof(x)<<3)-leading_zeros(x-one(x)))
nextpow2(x::Integer) = reinterpret(typeof(x),x < 0 ? -nextpow2(unsigned(-x)) : nextpow2(unsigned(x)))

prevpow2(x::Unsigned) = (one(x)>>(x==0)) << ((sizeof(x)<<3)-leading_zeros(x)-1)
prevpow2(x::Unsigned) = one(x) << unsigned((sizeof(x)<<3)-leading_zeros(x)-1)
prevpow2(x::Integer) = reinterpret(typeof(x),x < 0 ? -prevpow2(unsigned(-x)) : prevpow2(unsigned(x)))

ispow2(x::Integer) = x > 0 && count_ones(x) == 1
Expand Down
50 changes: 33 additions & 17 deletions base/operators.jl
Original file line number Diff line number Diff line change
Expand Up @@ -126,12 +126,16 @@ end
const .≤ = .<=
const .≠ = .!=

# core << >> and >>> takes Int as second arg
# Core <<, >>, and >>> take either Int or UInt as second arg. Signed shift
# counts can shift in either direction, and are translated here to unsigned
# counts. Integer datatypes only need to implement the unsigned version.

"""
<<(x, n)
Left bit shift operator, `x << n`. The result is `x` shifted left by `n` bits,
where `n >= 0`, filling with `0`s. This is equivalent to `x * 2^n`.
Left bit shift operator, `x << n`. For `n >= 0`, the result is `x` shifted left
by `n` bits, filling with `0`s. This is equivalent to `x * 2^n`. For `n < 0`,
this is equivalent to `x >> -n`.
```jldoctest
julia> Int8(3) << 2
Expand All @@ -145,14 +149,21 @@ julia> bits(Int8(12))
```
See also [`>>`](:func:`>>`), [`>>>`](:func:`>>>`).
"""
<<(x,y::Int) = no_op_err("<<", typeof(x))
function <<(x, c::Integer)
typemin(Int) <= c <= typemax(Int) && return x << (c % Int)
(x >= 0 || c >= 0) && return zero(x)
oftype(x, -1)
end
<<(x, c::Unsigned) = c <= typemax(UInt) ? x << (c % UInt) : zero(x)
<<(x, c::Int) = c >= 0 ? x << unsigned(c) : x >> unsigned(-c)

"""
>>(x, n)
Right bit shift operator, `x >> n`. The result is `x` shifted right by `n` bits, where
`n >= 0`, filling with `0`s if `x >= 0`, `1`s if `x < 0`, preserving the sign of `x`.
This is equivalent to `fld(x, 2^n)`.
Right bit shift operator, `x >> n`. For `n >= 0`, the result is `x` shifted
right by `n` bits, where `n >= 0`, filling with `0`s if `x >= 0`, `1`s if `x <
0`, preserving the sign of `x`. This is equivalent to `fld(x, 2^n)`. For `n <
0`, this is equivalent to `x << -n`.
```jldoctest
Expand All @@ -176,16 +187,23 @@ julia> bits(Int8(-4))
```
See also [`>>>`](:func:`>>>`), [`<<`](:func:`<<`).
"""
>>(x,y::Int) = no_op_err(">>", typeof(x))
function >>(x, c::Integer)
typemin(Int) <= c <= typemax(Int) && return x >> (c % Int)
(x >= 0 || c < 0) && return zero(x)
oftype(x, -1)
end
>>(x, c::Unsigned) = c <= typemax(UInt) ? x >> (c % UInt) : zero(x)
>>(x, c::Int) = c >= 0 ? x >> unsigned(c) : x << unsigned(-c)

"""
>>>(x, n)
Unsigned right bit shift operator, `x >>> n`. The result is `x` shifted right by `n` bits,
where `n >= 0`, filling with `0`s.
Unsigned right bit shift operator, `x >>> n`. For `n >= 0`, the result is `x`
shifted right by `n` bits, where `n >= 0`, filling with `0`s. For `n < 0`, this
is equivalent to `x [<<](:func:`<<`) -n`].
For `Unsigned` integer types, this is eqivalent to [`>>`](:func:`>>`). For `Signed` integer types,
this is equivalent to `(unsigned(x) >> n) % typeof(x)`.
For `Unsigned` integer types, this is eqivalent to [`>>`](:func:`>>`). For
`Signed` integer types, this is equivalent to `signed(unsigned(x) >> n)`.
```jldoctest
julia> Int8(-14) >>> 2
Expand All @@ -202,11 +220,9 @@ is equivalent to [`>>`](:func:`>>`).
See also [`>>`](:func:`>>`), [`<<`](:func:`<<`).
"""
>>>(x,y::Int) = no_op_err(">>>", typeof(x))

<<(x,y::Integer) = typemax(Int) < y ? zero(x) : x << (y % Int)
>>(x,y::Integer) = typemax(Int) < y ? zero(x) : x >> (y % Int)
>>>(x,y::Integer) = typemax(Int) < y ? zero(x) : x >>> (y % Int)
>>>(x, c::Integer) = typemin(Int) <= c <= typemax(Int) ? x >>> (c % Int) : zero(x)
>>>(x, c::Unsigned) = c <= typemax(UInt) ? x >>> (c % UInt) : zero(x)
>>>(x, c::Int) = c >= 0 ? x >>> unsigned(c) : x << unsigned(-c)

# fallback div, fld, and cld implementations
# NOTE: C89 fmod() and x87 FPREM implicitly provide truncating float division,
Expand Down
1 change: 0 additions & 1 deletion doc/stdlib/math.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2199,4 +2199,3 @@ some built-in integration support in Julia.
These quadrature rules work best for smooth functions within each interval, so if your function has a known discontinuity or other singularity, it is best to subdivide your interval to put the singularity at an endpoint. For example, if ``f`` has a discontinuity at ``x=0.7`` and you want to integrate from 0 to 1, you should use ``quadgk(f, 0,0.7,1)`` to subdivide the interval at the point of discontinuity. The integrand is never evaluated exactly at the endpoints of the intervals, so it is possible to integrate functions that diverge at the endpoints as long as the singularity is integrable (for example, a ``log(x)`` or ``1/sqrt(x)`` singularity).

For real-valued endpoints, the starting and/or ending points may be infinite. (A coordinate transformation is performed internally to map the infinite interval to a finite one.)

4 changes: 4 additions & 0 deletions test/bigint.jl
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,10 @@ end
@test BigInt(5) >> 1 == 2
@test BigInt(-5) << 3 == -40
@test BigInt(-5) >> 1 == -3
@test BigInt(5) >> -3 == 40
@test BigInt(5) << -1 == 2
@test BigInt(-5) >> -3 == -40
@test BigInt(-5) << -1 == -3

@test ~BigInt(123) == -124
@test BigInt(123) & BigInt(234) == 106
Expand Down
35 changes: 35 additions & 0 deletions test/int.jl
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,41 @@ for T in (UInt8, UInt16, UInt32, UInt64)
@test_throws InexactError convert(T, -1)
end

# Test bit shifts
for T in Base.BitInteger_types
nbits = 8*sizeof(T)
issigned = typemin(T) < 0
highbit = T(2) ^ (nbits-1)
val1 = 0x1234567890abcdef % T % highbit
val2 = val1 + highbit
for val in (val1, val2)
for count in 0:nbits+1
ucount, scount = unsigned(count), signed(count)
# Note: We assume modulo semantics for the arithmetic operations
# used here
if count < nbits
@test val << ucount === val * T(2)^count
@test val >>> ucount ===
fld(unsigned(val), unsigned(T(2))^count) % T
else
@test val << ucount === T(0)
@test val >>> ucount === T(0)
end
@test val << scount === val << ucount
@test val << -scount === val >> ucount
@test val >>> scount === val >>> ucount
@test val >>> -scount === val << ucount
if count < (issigned ? nbits-1 : nbits)
@test val >> ucount === fld(val, T(2)^count)
else
@test val >> ucount === T(val<0 ? -1 : 0)
end
@test val >> scount === val >> ucount
@test val >> -scount === val << ucount
end
end
end

@test widen(UInt8(3)) === UInt32(3)
@test widen(UInt16(3)) === UInt32(3)
@test widen(UInt32(3)) === UInt64(3)
Expand Down

0 comments on commit e3d5fda

Please sign in to comment.