Skip to content

Commit

Permalink
Low-level Constructors
Browse files Browse the repository at this point in the history
  • Loading branch information
jmkuhn committed May 18, 2020
1 parent ffd63b6 commit ce16ca4
Show file tree
Hide file tree
Showing 2 changed files with 152 additions and 0 deletions.
111 changes: 111 additions & 0 deletions src/DecFP.jl
Original file line number Diff line number Diff line change
Expand Up @@ -566,6 +566,43 @@ function Base.decompose(x::DecimalFloatingPoint)::Tuple{BigInt, Int, BigInt}
end
end

"""
Dec32(x::Union{Real, AbstractString} [, mode::RoundingMode])
Dec32([sign::Integer,] significand::Integer, exponent::Integer)
"""
function Dec32(sign::Integer, significand::Integer, exponent::Integer)
-1 <= sign <= 1 || throw(DomainError(sign, "sign must be -1, 0, or +1"))
significand == 0 && return flipsign(zero(Dec32), sign)
sign == 0 && throw(DomainError(sign, "sign must be -1 or +1 for non-zero significand"))
sb = signbit(sign) ? 0x80000000 : zero(UInt32)
e = exponent + 101
s = Int32(abs(significand))
while s >= 10000000 # Int(maxintfloat(Dec32))
q, r = divrem(s, Int32(10))
r != 0 && throw(InexactError(:Dec32, Dec32, (sign, significand, exponent)))
s = q
e += 1
end
while e > 191 && s < 1000000 # Int(maxintfloat(Dec32) / 10)
s *= Int32(10)
e -= 1
end
e > 191 && throw(InexactError(:Dec32, Dec32, (sign, significand, exponent)))
while e < 0
q, r = divrem(s, Int32(10))
r != 0 && throw(InexactError(:Dec32, Dec32, (sign, significand, exponent)))
s = q
e += 1
end
# check whether significand will fit in 23 bits
if s < 0x00200000
return reinterpret(Dec32, sb | (UInt32(e) << 23) | s)
end
return reinterpret(Dec32, sb | 0x60000000 | (UInt32(e) << 21) | (s & 0x001fffff))
end

Dec32(significand::Integer, exponent::Integer) = Dec32(sign(significand), significand, exponent)

function sigexp(x::Dec32)
n = reinterpret(UInt32, x)
if n & 0x60000000 == 0x60000000
Expand All @@ -579,6 +616,43 @@ function sigexp(x::Dec32)
return s, e
end

"""
Dec64(x::Union{Real, AbstractString} [, mode::RoundingMode])
Dec64([sign::Integer,] significand::Integer, exponent::Integer)
"""
function Dec64(sign::Integer, significand::Integer, exponent::Integer)
-1 <= sign <= 1 || throw(DomainError(sign, "sign must be -1, 0, or +1"))
significand == 0 && return flipsign(zero(Dec64), sign)
sign == 0 && throw(DomainError(sign, "sign must be -1 or +1 for non-zero significand"))
sb = signbit(sign) ? 0x8000000000000000 : zero(UInt64)
e = exponent + 398
s = Int64(abs(significand))
while s >= 10000000000000000 # Int64(maxintfloat(Dec64))
q, r = divrem(s, 10)
r != 0 && throw(InexactError(:Dec64, Dec64, (sign, significand, exponent)))
s = q
e += 1
end
while e > 767 && s < 1000000000000000 # Int64(maxintfloat(Dec64) / 10)
s *= 10
e -= 1
end
e > 767 && throw(InexactError(:Dec64, Dec64, (sign, significand, exponent)))
while e < 0
q, r = divrem(s, 10)
r != 0 && throw(InexactError(:Dec64, Dec64, (sign, significand, exponent)))
s = q
e += 1
end
# check whether significand will fit in 53 bits
if s < 0x0020000000000000
return reinterpret(Dec64, sb | (UInt64(e) << 53) | s)
end
return reinterpret(Dec64, sb | 0x6000000000000000 | (UInt64(e) << 51) | (s & 0x0007ffffffffffff))
end

Dec64(significand::Integer, exponent::Integer) = Dec64(sign(significand), significand, exponent)

function sigexp(x::Dec64)
n = reinterpret(UInt64, x)
if n & 0x6000000000000000 == 0x6000000000000000
Expand All @@ -592,6 +666,43 @@ function sigexp(x::Dec64)
return s, e
end

"""
Dec128(x::Union{Real, AbstractString} [, mode::RoundingMode])
Dec128([sign::Integer,] significand::Integer, exponent::Integer)
"""
function Dec128(sign::Integer, significand::Integer, exponent::Integer)
-1 <= sign <= 1 || throw(DomainError(sign, "sign must be -1, 0, or +1"))
significand == 0 && return flipsign(zero(Dec128), sign)
sign == 0 && throw(DomainError(sign, "sign must be -1 or +1 for non-zero significand"))
sb = signbit(sign) ? 0x80000000000000000000000000000000 : zero(UInt128)
e = exponent + 6176
s = Int128(abs(significand))
while s >= 10000000000000000000000000000000000 # Int128(maxintfloat(Dec128))
q, r = divrem(s, 10)
r != 0 && throw(InexactError(:Dec128, Dec128, (sign, significand, exponent)))
s = q
e += 1
end
while e > 12287 && s < 1000000000000000000000000000000000 # Int128(maxintfloat(Dec128) / 10)
s *= 10
e -= 1
end
e > 12287 && throw(InexactError(:Dec128, Dec128, (sign, significand, exponent)))
while e < 0
q, r = divrem(s, 10)
r != 0 && throw(InexactError(:Dec128, Dec128, (sign, significand, exponent)))
s = q
e += 1
end
# check whether significand will fit in 113 bits
if s < 0x00020000000000000000000000000000
return reinterpret(Dec128, sb | (UInt128(e) << 113) | s)
end
return reinterpret(Dec128, sb | 0x60000000000000000000000000000000 | (UInt128(e) << 111) | (s & 0x00007fffffffffffffffffffffffffff))
end

Dec128(significand::Integer, exponent::Integer) = Dec128(sign(significand), significand, exponent)

function sigexp(x::Dec128)
n = reinterpret(UInt128, x)
if n & 0x60000000000000000000000000000000 == 0x60000000000000000000000000000000
Expand Down
41 changes: 41 additions & 0 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -26,18 +26,48 @@ for T in (Dec32, Dec64, Dec128)

if T == Dec32
@test d32"3.2" * d32"4.5" == d32"14.4"
@test T(1, Int32(maxintfloat(T)), 0) == maxintfloat(T)
@test T(1, Int32(maxintfloat(T) * 100), -2) == maxintfloat(T)
@test T(1, Int32(maxintfloat(T) - 1), 90) == floatmax(T)
@test_throws InexactError T(1, Int32(maxintfloat(T)), 90)
@test_throws InexactError T(1, Int32(maxintfloat(T)) + 1, 0)
@test T(1, 1, 96) == T("1e96")
@test T(1, 1, -101) == nextfloat(T(0))
@test T(1, 10, -102) == nextfloat(T(0))
@test_throws InexactError T(1, 1, -102)
@test_throws InexactError T(1, 11, -102)
@test eps(Dec32) == parse(Dec32, "1e-6")
@test floatmax(Dec32) == parse(Dec32, "+9999999E+90")
@test bswap(Dec32(1.5)) == reinterpret(Dec32, bswap(reinterpret(UInt32, Dec32(1.5))))
elseif T == Dec64
@test d"3.2" * d"4.5" == d"14.4"
@test d64"3.2" * d64"4.5" == d64"14.4"
@test T(1, Int64(maxintfloat(T)), 0) == maxintfloat(T)
@test T(1, Int64(maxintfloat(T) * 100), -2) == maxintfloat(T)
@test T(1, Int64(maxintfloat(T) - 1), 369) == floatmax(T)
@test_throws InexactError T(1, Int64(maxintfloat(T)), 369)
@test_throws InexactError T(1, Int64(maxintfloat(T)) + 1, 0)
@test T(1, 1, 384) == T("1e384")
@test T(1, 1, -398) == nextfloat(T(0))
@test T(1, 10, -399) == nextfloat(T(0))
@test_throws InexactError T(1, 1, -399)
@test_throws InexactError T(1, 11, -399)
@test eps(Dec64) == parse(Dec64, "1e-15")
@test floatmax(Dec64) == parse(Dec64, "+9999999999999999E+369")
@test bswap(Dec64(1.5)) == reinterpret(Dec64, bswap(reinterpret(UInt64, Dec64(1.5))))
else
@test d128"3.2" * d128"4.5" == d128"14.4"
@test eps(Dec128) == parse(Dec128, "1e-33")
@test T(1, 10000000000000000000000000000000000, 0) == maxintfloat(T)
@test T(1, 10000000000000000000000000000000000 * 100, -2) == maxintfloat(T)
@test T(1, 10000000000000000000000000000000000 - 1, 6111) == floatmax(T)
@test_throws InexactError T(1, 10000000000000000000000000000000000, 6111)
@test_throws InexactError T(1, 10000000000000000000000000000000000 + 1, 0)
@test T(1, 1, 6144) == T("1e6144")
@test T(1, 1, -6176) == nextfloat(T(0))
@test T(1, 10, -6177) == nextfloat(T(0))
@test_throws InexactError T(1, 1, -6177)
@test_throws InexactError T(1, 11, -6177)
@test floatmax(Dec128) == parse(Dec128, "+9999999999999999999999999999999999E+6111")
@test bswap(Dec128(1.5)) == reinterpret(Dec128, bswap(reinterpret(UInt128, Dec128(1.5))))
end
Expand Down Expand Up @@ -74,6 +104,17 @@ for T in (Dec32, Dec64, Dec128)
@test T("0.9999999999999999999999999999999999999999", RoundUp) == T(1)
@test T("0.9999999999999999999999999999999999999999", RoundDown) == prevfloat(T(1))

@test T(0, 0, 0) == T(0)
@test T(1, 0, 10) == T(0)
@test isequal(T(-1, 0, 0), T(-0.0))
@test T(1, 125, -2) == T(1.25)
@test T(-1, 500, -2) == T(-5)
@test T(1, 5, 2) == T(500)
@test_throws DomainError T(0, 1, 0)
@test_throws DomainError T(2, 1, 0)
@test T(125, -2) == T(1.25)
@test T(-125, -2) == T(-1.25)

io = IOBuffer()
show(io, T("NaN")); @test String(take!(io)) == "NaN"
show(io, T("Inf")); @test String(take!(io)) == "Inf"
Expand Down

0 comments on commit ce16ca4

Please sign in to comment.