From a794920df747ef7d88706059fa3747f20f9d4ede Mon Sep 17 00:00:00 2001 From: John M Kuhn Date: Mon, 18 May 2020 08:14:21 -0400 Subject: [PATCH 1/2] Low-level Constructors --- src/DecFP.jl | 111 +++++++++++++++++++++++++++++++++++++++++++++++ test/runtests.jl | 41 +++++++++++++++++ 2 files changed, 152 insertions(+) diff --git a/src/DecFP.jl b/src/DecFP.jl index 80226f3..e26b331 100644 --- a/src/DecFP.jl +++ b/src/DecFP.jl @@ -587,6 +587,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 @@ -600,6 +637,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 @@ -613,6 +687,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 a5f4868..8b0b920 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" From a7542c389eb9d9321de5e184b395598b1118952d Mon Sep 17 00:00:00 2001 From: John M Kuhn Date: Sun, 24 May 2020 19:10:19 -0400 Subject: [PATCH 2/2] Unify low-level constructors; expand doc string --- src/DecFP.jl | 186 +++++++++++++++++++---------------------------- test/runtests.jl | 16 ++-- 2 files changed, 81 insertions(+), 121 deletions(-) diff --git a/src/DecFP.jl b/src/DecFP.jl index e26b331..f2d45ba 100644 --- a/src/DecFP.jl +++ b/src/DecFP.jl @@ -124,6 +124,81 @@ for w in (32,64,128) end end + @eval function $BID(sign::Integer, significand::Integer, exponent::Integer) + -1 <= sign <= 1 || throw(DomainError(sign, "sign must be -1, 0, or +1")) + significand == 0 && return flipsign(zero($BID), sign) + sign == 0 && throw(DomainError(sign, "sign must be -1 or +1 for non-zero significand")) + p = 9 * $w ÷ 32 - 2 + emax = 3 * 2^($w ÷ 16 + 3) + bias = emax + p - 2 + bemax = emax * 2 - 1 + ebits = $w ÷ 16 + 6 + sbits = $w - ebits - 1 + sb = signbit(sign) ? one($Ti) << ($w - 1) : zero($Ti) + e = exponent + bias + s = $Ti(abs(significand)) + while s >= $Ti(maxintfloat($BID)) + q, r = divrem(s, $Ti(10)) + r != 0 && throw(InexactError(Symbol($BID), $BID, (sign, significand, exponent))) + s = q + e += 1 + end + while e > bemax && s < $Ti(maxintfloat($BID)) / 10 + s *= $Ti(10) + e -= 1 + end + e > bemax && throw(InexactError(Symbol($BID), $BID, (sign, significand, exponent))) + while e < 0 + q, r = divrem(s, $Ti(10)) + r != 0 && throw(InexactError(Symbol($BID), $BID, (sign, significand, exponent))) + s = q + e += 1 + end + if s < one($Ti) << sbits + return reinterpret($BID, sb | ($Ti(e) << sbits) | s) + end + return reinterpret($BID, sb | ($Ti(0x3) << ($w - 3)) | ($Ti(e) << (sbits - 2)) | (s & (typemax($Ti) >> (ebits + 3)))) + end + + @eval $BID(significand::Integer, exponent::Integer) = $BID(sign(significand), significand, exponent) + + @eval @doc """ + $($BID)(x::Union{Real, AbstractString} [, mode::RoundingMode]) + $($BID)([sign::Integer,] significand::Integer, exponent::Integer) + + Create a $($w)-bit IEEE 754-2008 decimal floating point number. The `mode` argument + specifies the direction in which the result should be rounded if the conversion cannot + be done exactly. If not provided, the `mode` is set by the current + `rounding(DecFP.DecimalFloatingPoint)` mode. + + `$($BID)(x::Real)` is the same as `convert($($BID), x)`. + + `$($BID)(x::AbstractString)` is the same as `parse($($BID), x)`. This is provided for + convenience since decimal literals are converted to `Float64` when parsed and may not + produce what you expect. + + `$($BID)(sign, significand, exponent)` returns `sign * significand * 10^exponent`. + If `sign` isn't passed, use the sign of `significand`. + + # Examples + ```julia-repl + julia> $($BID)(1) + 1.0 + + julia> $($BID)(1.5) + 1.5 + + julia> $($BID)("0.99999999999999999999999999999999999") + 1.0 + + julia> $($BID)("0.99999999999999999999999999999999999", RoundDown) + 0.$("9"^(9*$w÷32-2)) + + julia> $($BID)(-1, 123456, -4) + -12.3456 + ``` + """ $BID + # fix method ambiguities: @eval $BID(x::Rational{T}) where {T} = convert($BID, x) end @@ -587,43 +662,6 @@ 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 @@ -637,43 +675,6 @@ 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 @@ -687,43 +688,6 @@ 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 8b0b920..de6b10c 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -26,8 +26,6 @@ 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) @@ -42,8 +40,6 @@ for T in (Dec32, Dec64, Dec128) 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) @@ -57,17 +53,15 @@ for T in (Dec32, Dec64, Dec128) @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, Int128(maxintfloat(T)) - 1, 6111) == floatmax(T) + @test_throws InexactError T(1, Int128(maxintfloat(T)), 6111) + @test_throws InexactError T(1, Int128(maxintfloat(T)) + 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 eps(Dec128) == parse(Dec128, "1e-33") @test floatmax(Dec128) == parse(Dec128, "+9999999999999999999999999999999999E+6111") @test bswap(Dec128(1.5)) == reinterpret(Dec128, bswap(reinterpret(UInt128, Dec128(1.5)))) end @@ -114,6 +108,8 @@ for T in (Dec32, Dec64, Dec128) @test_throws DomainError T(2, 1, 0) @test T(125, -2) == T(1.25) @test T(-125, -2) == T(-1.25) + @test T(1, Int128(maxintfloat(T)), 0) == maxintfloat(T) + @test T(1, Int128(maxintfloat(T)) * 100, -2) == maxintfloat(T) io = IOBuffer() show(io, T("NaN")); @test String(take!(io)) == "NaN"