diff --git a/src/DecFP.jl b/src/DecFP.jl index f542a6b..db35246 100644 --- a/src/DecFP.jl +++ b/src/DecFP.jl @@ -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 @@ -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 @@ -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 diff --git a/test/runtests.jl b/test/runtests.jl index b3fab5b..a9fb71b 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -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 @@ -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"