Skip to content

Commit

Permalink
BigInt -> Int128 not throwing InexactError.
Browse files Browse the repository at this point in the history
As suggested by @StefanKarpinski, we can make use of the GMP internal representation for the conversions, which doesn't require creating new BigInts.
  • Loading branch information
simonbyrne committed Mar 26, 2015
1 parent efb878e commit b26c26c
Show file tree
Hide file tree
Showing 2 changed files with 27 additions and 51 deletions.
73 changes: 22 additions & 51 deletions base/gmp.jl
Original file line number Diff line number Diff line change
Expand Up @@ -148,57 +148,37 @@ convert(::Type{BigInt}, x::Integer) = BigInt(x)
convert(::Type{BigInt}, x::Float16) = BigInt(x)
convert(::Type{BigInt}, x::FloatingPoint) = BigInt(x)

function convert(::Type{Int64}, x::BigInt)
lo = Int64(convert(Culong, x & typemax(UInt32)))
hi = Int64(convert(Clong, x >> 32))
hi << 32 | lo
end
convert(::Type{Int32}, n::BigInt) = convert(Int32,convert(Clong, n))
convert(::Type{Int16}, n::BigInt) = convert(Int16,convert(Clong, n))
convert(::Type{Int8}, n::BigInt) = convert(Int8,convert(Clong, n))

function convert(::Type{Clong}, n::BigInt)
fits = ccall((:__gmpz_fits_slong_p, :libgmp), Int32, (Ptr{BigInt},), &n) != 0
if fits
ccall((:__gmpz_get_si, :libgmp), Clong, (Ptr{BigInt},), &n)
else
throw(InexactError())
end
end

function convert(::Type{UInt64}, x::BigInt)
lo = UInt64(convert(Culong, x & typemax(UInt32)))
hi = UInt64(convert(Culong, x >> 32))
hi << 32 | lo
rem(x::BigInt, ::Type{Bool}) = ((x&1)!=0)
function rem{T<:Union(Unsigned,Signed)}(x::BigInt, ::Type{T})
u = zero(T)
for l = 1:min(abs(x.size), cld(sizeof(T),sizeof(Limb)))
u += (unsafe_load(x.d,l)%T) << ((sizeof(Limb)<<3)*(l-1))
end
x.size < 0 ? -u : u
end
convert(::Type{UInt32}, x::BigInt) = convert(UInt32,convert(Culong, x))
convert(::Type{UInt16}, x::BigInt) = convert(UInt16,convert(Culong, x))
convert(::Type{UInt8}, x::BigInt) = convert(UInt8,convert(Culong, x))

function convert(::Type{Culong}, n::BigInt)
fits = ccall((:__gmpz_fits_ulong_p, :libgmp), Int32, (Ptr{BigInt},), &n) != 0
if fits
ccall((:__gmpz_get_ui, :libgmp), Culong, (Ptr{BigInt},), &n)
function convert{T<:Unsigned}(::Type{T}, x::BigInt)
if sizeof(T) < sizeof(Limb)
convert(T, convert(Limb,x))
else
throw(InexactError())
0 <= x.size <= cld(sizeof(T),sizeof(Limb)) || throw(InexactError())
x % T
end
end

if sizeof(Int32) == sizeof(Clong)
function convert(::Type{UInt128}, x::BigInt)
UInt128(UInt(x>>>96))<<96 +
UInt128(UInt((x>>>64) & typemax(UInt32)))<<64 +
UInt128(UInt((x>>>32) & typemax(UInt32)))<<32 +
UInt128(UInt(x & typemax(UInt32)))
end
end
if sizeof(Int64) == sizeof(Clong)
function convert(::Type{UInt128}, x::BigInt)
UInt128(UInt(x>>>64))<<64 +
UInt128(UInt(x & typemax(UInt64)))
function convert{T<:Signed}(::Type{T}, x::BigInt)
n = abs(x.size)
if sizeof(T) < sizeof(Limb)
SLimb = typeof(Signed(one(Limb)))
convert(T, convert(SLimb, x))
else
0 <= n <= cld(sizeof(T),sizeof(Limb)) || throw(InexactError())
y = x % T
(x.size > 0) $ (y > 0) && throw(InexactError()) # catch overflow
y
end
end
convert(::Type{Int128}, x::BigInt) = copysign(UInt128(abs(x))%Int128,x)


function call(::Type{Float64}, n::BigInt, ::RoundingMode{:ToZero})
Expand Down Expand Up @@ -243,15 +223,6 @@ convert(::Type{Float64}, n::BigInt) = Float64(n,RoundNearest)
convert(::Type{Float32}, n::BigInt) = Float32(n,RoundNearest)
convert(::Type{Float16}, n::BigInt) = Float16(n,RoundNearest)

rem(x::BigInt, ::Type{Bool}) = ((x&1)!=0)

rem{T<:Unsigned}(n::BigInt, ::Type{T}) = convert(T, n & typemax(T))
function rem{T<:Integer}(n::BigInt, ::Type{T})
lo, hi = typemin(T), typemax(T)
convert(T, (n-lo) & (widen(hi)-widen(lo)) + lo)
end


promote_rule{T<:Integer}(::Type{BigInt}, ::Type{T}) = BigInt

# serialization
Expand Down
5 changes: 5 additions & 0 deletions test/numbers.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2401,3 +2401,8 @@ for T in Any[Int16, Int32, UInt32, Int64, UInt64, BigInt]
end

@test_throws InexactError UInt128(-1)

for T in (Int8,Int16,Int32,Int64,Int128,UInt8,UInt16,UInt32,UInt64,UInt128)
@test_throws InexactError T(big(typemax(T))+1)
@test_throws InexactError T(big(typemin(T))-1)
end

0 comments on commit b26c26c

Please sign in to comment.