From 2e71f3e4796cf44df37a38ae56b7b3034a31339d Mon Sep 17 00:00:00 2001 From: Mohamad Barbar Date: Thu, 16 Jan 2025 17:56:11 +1100 Subject: [PATCH 01/19] Fix ambiguities. --- src/FixedPointDecimals.jl | 46 +++++++++++++++++++++++++++++++-------- 1 file changed, 37 insertions(+), 9 deletions(-) diff --git a/src/FixedPointDecimals.jl b/src/FixedPointDecimals.jl index 28b8611..3e7e279 100644 --- a/src/FixedPointDecimals.jl +++ b/src/FixedPointDecimals.jl @@ -397,6 +397,36 @@ end # --- Checked arithmetic --- +function Base.add_with_overflow(x::T, y::T) where {T<:FD} + z, b = Base.add_with_overflow(x.i, y.i) + return (reinterpret(T, z), b) +end +Base.Checked.add_with_overflow(x::FD, y::FD) = Base.Checked.add_with_overflow(promote(x, y)...) +Base.Checked.add_with_overflow(x::FD, y) = Base.Checked.add_with_overflow(promote(x, y)...) +Base.Checked.add_with_overflow(x, y::FD) = Base.Checked.add_with_overflow(promote(x, y)...) + +function Base.sub_with_overflow(x::T, y::T) where {T<:FD} + z, b = Base.sub_with_overflow(x.i, y.i) + return (reinterpret(T, z), b) +end +Base.Checked.sub_with_overflow(x::FD, y::FD) = Base.Checked.sub_with_overflow(promote(x, y)...) +Base.Checked.sub_with_overflow(x::FD, y) = Base.Checked.sub_with_overflow(promote(x, y)...) +Base.Checked.sub_with_overflow(x, y::FD) = Base.Checked.sub_with_overflow(promote(x, y)...) + +function Base.Checked.mul_with_overflow(x::FD{T,f}, y::FD{T,f}) where {T<:Integer,f} + powt = coefficient(FD{T, f}) + quotient, remainder = fldmodinline(_widemul(x.i, y.i), powt) + v = _round_to_nearest(quotient, remainder, powt) + return if typemin(T) <= v <= typemax(T) + return (reinterpret(FD{T,f}, T(v)), false) + else + return (reinterpret(FD{T,f}, Base.trunc_int(T, v)), true) + end +end +Base.Checked.mul_with_overflow(x::FD, y::FD) = Base.Checked.mul_with_overflow(promote(x, y)...) +Base.Checked.mul_with_overflow(x::FD, y) = Base.Checked.mul_with_overflow(promote(x, y)...) +Base.Checked.mul_with_overflow(x, y::FD) = Base.Checked.mul_with_overflow(promote(x, y)...) + Base.checked_add(x::FD, y::FD) = Base.checked_add(promote(x, y)...) Base.checked_sub(x::FD, y::FD) = Base.checked_sub(promote(x, y)...) Base.checked_mul(x::FD, y::FD) = Base.checked_mul(promote(x, y)...) @@ -424,21 +454,19 @@ Base.checked_mod(x::FD, y) = Base.checked_mod(promote(x, y)...) Base.checked_mod(x, y::FD) = Base.checked_mod(promote(x, y)...) function Base.checked_add(x::T, y::T) where {T<:FD} - z, b = Base.add_with_overflow(x.i, y.i) + z, b = Base.Checked.add_with_overflow(x, y) b && Base.Checked.throw_overflowerr_binaryop(:+, x, y) - return reinterpret(T, z) + return z end function Base.checked_sub(x::T, y::T) where {T<:FD} - z, b = Base.sub_with_overflow(x.i, y.i) + z, b = Base.Checked.sub_with_overflow(x, y) b && Base.Checked.throw_overflowerr_binaryop(:-, x, y) - return reinterpret(T, z) + return z end function Base.checked_mul(x::FD{T,f}, y::FD{T,f}) where {T<:Integer,f} - powt = coefficient(FD{T, f}) - quotient, remainder = fldmodinline(_widemul(x.i, y.i), powt) - v = _round_to_nearest(quotient, remainder, powt) - typemin(T) <= v <= typemax(T) || Base.Checked.throw_overflowerr_binaryop(:*, x, y) - return reinterpret(FD{T, f}, T(v)) + z, b = Base.Checked.mul_with_overflow(x, y) + b && Base.Checked.throw_overflowerr_binaryop(:*, x, y) + return z end # Checked division functions for divfn in [:div, :fld, :cld] From 2297c6f89689e89527d894e8113666ee7e91be08 Mon Sep 17 00:00:00 2001 From: Mohamad Barbar Date: Thu, 16 Jan 2025 18:25:30 +1100 Subject: [PATCH 02/19] Add tests. --- test/FixedDecimal.jl | 76 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/test/FixedDecimal.jl b/test/FixedDecimal.jl index 18d87d7..7290760 100644 --- a/test/FixedDecimal.jl +++ b/test/FixedDecimal.jl @@ -776,6 +776,82 @@ end @test FD{Int8,1}(2) / Int8(20) == FD{Int8,1}(0.1) end + @testset "limits: with_overflow math" begin + # Easy to reason about cases of overflow: + @test Base.Checked.add_with_overflow(FD{Int8,2}(1), FD{Int8,2}(1)) == (FD{Int8,2}(-0.56), true) + @test Base.Checked.add_with_overflow(FD{Int8,2}(1), 1) == (FD{Int8,2}(-0.56), true) + @test Base.Checked.add_with_overflow(FD{Int8,2}(1), FD{Int8,2}(0.4)) == (FD{Int8,2}(-1.16), true) + + @test Base.Checked.sub_with_overflow(FD{Int8,2}(1), FD{Int8,2}(-1)) == (FD{Int8,2}(-0.56), true) + @test Base.Checked.sub_with_overflow(1, FD{Int8,2}(-1)) == (FD{Int8,2}(-0.56), true) + @test Base.Checked.sub_with_overflow(FD{Int8,2}(-1), FD{Int8,2}(0.4)) == (FD{Int8,2}(1.16), true) + + @test Base.Checked.mul_with_overflow(FD{Int8,2}(1.2), FD{Int8,2}(1.2)) == (FD{Int8,2}(-1.12), true) + @test Base.Checked.mul_with_overflow(FD{Int8,1}(12), 2) == (FD{Int8,1}(-1.6), true) + @test Base.Checked.mul_with_overflow(FD{Int8,0}(120), 2) == (FD{Int8,0}(-16), true) + @test Base.Checked.mul_with_overflow(120, FD{Int8,0}(2)) == (FD{Int8,0}(-16), true) + + @testset "with_overflow math corner cases" begin + @testset for I in (Int128, UInt128, Int8, UInt8), f in (0,2) + T = FD{I, f} + issigned(I) = signed(I) === I + + @test Base.Checked.add_with_overflow(typemax(T), eps(T)) == (typemax(T) + eps(T), true) + issigned(I) && @test Base.Checked.add_with_overflow(typemin(T), -eps(T)) == (typemin(T) + -eps(T), true) + @test Base.Checked.add_with_overflow(typemax(T), 1) == (typemax(T) + 1, true) + @test Base.Checked.add_with_overflow(1, typemax(T)) == (typemax(T) + 1, true) + + @test Base.Checked.sub_with_overflow(typemin(T), eps(T)) == (typemin(T) - eps(T), true) + issigned(I) && @test Base.Checked.sub_with_overflow(typemax(T), -eps(T)) == (typemax(T) - -eps(T), true) + @test Base.Checked.sub_with_overflow(typemin(T), 1) == (typemin(T) - 1, true) + if issigned(I) && 2.0 <= typemax(T) + @test Base.Checked.sub_with_overflow(-2, typemax(T)) == (-2 -typemax(T), true) + end + + @test Base.Checked.mul_with_overflow(typemax(T), typemax(T)) == (typemax(T) * typemax(T), true) + issigned(I) && @test Base.Checked.mul_with_overflow(typemin(T), typemax(T)) == (typemin(T) * typemax(T), true) + if 2.0 <= typemax(T) + @test Base.Checked.mul_with_overflow(typemax(T), 2) == (typemax(T) * 2, true) + @test Base.Checked.mul_with_overflow(2, typemax(T)) == (2 * typemax(T), true) + issigned(I) && @test Base.Checked.mul_with_overflow(typemin(T), 2) == (typemin(T) * 2, true) + issigned(I) && @test Base.Checked.mul_with_overflow(2, typemin(T)) == (2 * typemin(T), true) + end + end + end + + @testset "with_overflow math promotions" begin + x = FD{Int8,1}(1) + y = FD{Int64,1}(2) + @testset for op in ( + Base.Checked.add_with_overflow, Base.Checked.sub_with_overflow, + Base.Checked.mul_with_overflow, + ) + @test op(x, y) === op(FD{Int64,1}(1), y) + @test op(y, x) === op(y, FD{Int64,1}(1)) + + @test op(x, 2) === op(x, FD{Int8,1}(2)) + @test op(2, x) === op(FD{Int8,1}(2), x) + end + end + + @testset "non-overflow with_overflow math" begin + @test Base.Checked.add_with_overflow(1, FD{Int8,1}(1.1)) == (FD{Int8,1}(2.1), false) + @test Base.Checked.add_with_overflow(FD{Int8,1}(1.1), 1) == (FD{Int8,1}(2.1), false) + @test Base.Checked.add_with_overflow(FD{Int64,8}(30.123), FD{Int64,8}(30)) == (FD{Int64,8}(60.123), false) + @test Base.Checked.add_with_overflow(FD{Int64,8}(30.123), FD{Int64,5}(30)) == (FD{Int64,8}(60.123), false) + + @test Base.Checked.sub_with_overflow(3, FD{Int16,2}(2.5)) == (FD{Int16,1}(0.5), false) + @test Base.Checked.sub_with_overflow(FD{Int16,2}(2.5), 3) == (FD{Int16,1}(-0.5), false) + @test Base.Checked.sub_with_overflow(FD{Int32,4}(10.11), FD{Int32,4}(2)) == (FD{Int32,4}(8.11), false) + @test Base.Checked.sub_with_overflow(FD{Int32,4}(10.11), FD{Int32,3}(2)) == (FD{Int32,4}(8.11), false) + + @test Base.Checked.mul_with_overflow(4, FD{Int64,6}(2.22)) == (FD{Int64,6}(8.88), false) + @test Base.Checked.mul_with_overflow(FD{Int64,6}(2.22), 4) == (FD{Int64,6}(8.88), false) + @test Base.Checked.mul_with_overflow(FD{Int128,14}(10), FD{Int128,14}(20.1)) == (FD{Int128,14}(201), false) + @test Base.Checked.mul_with_overflow(FD{Int128,30}(10.11), FD{Int128,0}(1)) == (FD{Int128,30}(10.11), false) + end + end + @testset "limits: overflow checked math" begin # Easy to reason about cases of overflow: @test_throws OverflowError Base.checked_add(FD{Int8,2}(1), FD{Int8,2}(1)) From c7e08262a525db8cd945c46575f90be5d9526a28 Mon Sep 17 00:00:00 2001 From: Mohamad Barbar Date: Thu, 16 Jan 2025 18:29:30 +1100 Subject: [PATCH 03/19] Comment --- test/FixedDecimal.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/FixedDecimal.jl b/test/FixedDecimal.jl index 7290760..a4520d6 100644 --- a/test/FixedDecimal.jl +++ b/test/FixedDecimal.jl @@ -834,7 +834,7 @@ end end end - @testset "non-overflow with_overflow math" begin + @testset "non-overflowing with_overflow math" begin @test Base.Checked.add_with_overflow(1, FD{Int8,1}(1.1)) == (FD{Int8,1}(2.1), false) @test Base.Checked.add_with_overflow(FD{Int8,1}(1.1), 1) == (FD{Int8,1}(2.1), false) @test Base.Checked.add_with_overflow(FD{Int64,8}(30.123), FD{Int64,8}(30)) == (FD{Int64,8}(60.123), false) From 112d5e0afb8bcc79d5de7cdf39d2631c88394f07 Mon Sep 17 00:00:00 2001 From: Mohamad Barbar Date: Thu, 16 Jan 2025 18:51:14 +1100 Subject: [PATCH 04/19] Simplify logic. --- src/FixedPointDecimals.jl | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/FixedPointDecimals.jl b/src/FixedPointDecimals.jl index 3e7e279..6286d03 100644 --- a/src/FixedPointDecimals.jl +++ b/src/FixedPointDecimals.jl @@ -417,11 +417,7 @@ function Base.Checked.mul_with_overflow(x::FD{T,f}, y::FD{T,f}) where {T<:Intege powt = coefficient(FD{T, f}) quotient, remainder = fldmodinline(_widemul(x.i, y.i), powt) v = _round_to_nearest(quotient, remainder, powt) - return if typemin(T) <= v <= typemax(T) - return (reinterpret(FD{T,f}, T(v)), false) - else - return (reinterpret(FD{T,f}, Base.trunc_int(T, v)), true) - end + return (reinterpret(FD{T,f}, Base.trunc_int(T, v)), v < typemin(T) || v > typemax(T)) end Base.Checked.mul_with_overflow(x::FD, y::FD) = Base.Checked.mul_with_overflow(promote(x, y)...) Base.Checked.mul_with_overflow(x::FD, y) = Base.Checked.mul_with_overflow(promote(x, y)...) From b43473b97985c8b0fde162cc0f2c900d6ab549ca Mon Sep 17 00:00:00 2001 From: Mohamad Barbar Date: Sat, 25 Jan 2025 19:37:06 +1100 Subject: [PATCH 05/19] Remove promoting with_overflow functions. --- src/FixedPointDecimals.jl | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/FixedPointDecimals.jl b/src/FixedPointDecimals.jl index 2a69abd..0e136a8 100644 --- a/src/FixedPointDecimals.jl +++ b/src/FixedPointDecimals.jl @@ -419,17 +419,11 @@ function Base.add_with_overflow(x::T, y::T) where {T<:FD} z, b = Base.add_with_overflow(x.i, y.i) return (reinterpret(T, z), b) end -Base.Checked.add_with_overflow(x::FD, y::FD) = Base.Checked.add_with_overflow(promote(x, y)...) -Base.Checked.add_with_overflow(x::FD, y) = Base.Checked.add_with_overflow(promote(x, y)...) -Base.Checked.add_with_overflow(x, y::FD) = Base.Checked.add_with_overflow(promote(x, y)...) function Base.sub_with_overflow(x::T, y::T) where {T<:FD} z, b = Base.sub_with_overflow(x.i, y.i) return (reinterpret(T, z), b) end -Base.Checked.sub_with_overflow(x::FD, y::FD) = Base.Checked.sub_with_overflow(promote(x, y)...) -Base.Checked.sub_with_overflow(x::FD, y) = Base.Checked.sub_with_overflow(promote(x, y)...) -Base.Checked.sub_with_overflow(x, y::FD) = Base.Checked.sub_with_overflow(promote(x, y)...) function Base.Checked.mul_with_overflow(x::FD{T,f}, y::FD{T,f}) where {T<:Integer,f} powt = coefficient(FD{T, f}) @@ -437,9 +431,6 @@ function Base.Checked.mul_with_overflow(x::FD{T,f}, y::FD{T,f}) where {T<:Intege v = _round_to_nearest(quotient, remainder, powt) return (reinterpret(FD{T,f}, Base.trunc_int(T, v)), v < typemin(T) || v > typemax(T)) end -Base.Checked.mul_with_overflow(x::FD, y::FD) = Base.Checked.mul_with_overflow(promote(x, y)...) -Base.Checked.mul_with_overflow(x::FD, y) = Base.Checked.mul_with_overflow(promote(x, y)...) -Base.Checked.mul_with_overflow(x, y::FD) = Base.Checked.mul_with_overflow(promote(x, y)...) Base.checked_add(x::FD, y::FD) = Base.checked_add(promote(x, y)...) Base.checked_sub(x::FD, y::FD) = Base.checked_sub(promote(x, y)...) From 48f1d8ddcd63193c313c588cc3fe9437982999dc Mon Sep 17 00:00:00 2001 From: Mohamad Barbar Date: Sat, 25 Jan 2025 21:25:26 +1100 Subject: [PATCH 06/19] Update tests. --- test/FixedDecimal.jl | 57 ++++++++++++++------------------------------ 1 file changed, 18 insertions(+), 39 deletions(-) diff --git a/test/FixedDecimal.jl b/test/FixedDecimal.jl index a1dd471..f13540b 100644 --- a/test/FixedDecimal.jl +++ b/test/FixedDecimal.jl @@ -779,17 +779,11 @@ end @testset "limits: with_overflow math" begin # Easy to reason about cases of overflow: @test Base.Checked.add_with_overflow(FD{Int8,2}(1), FD{Int8,2}(1)) == (FD{Int8,2}(-0.56), true) - @test Base.Checked.add_with_overflow(FD{Int8,2}(1), 1) == (FD{Int8,2}(-0.56), true) + @test Base.Checked.add_with_overflow(FD{Int8,2}(1), FD{Int8,2}(1)) == (FD{Int8,2}(-0.56), true) @test Base.Checked.add_with_overflow(FD{Int8,2}(1), FD{Int8,2}(0.4)) == (FD{Int8,2}(-1.16), true) - @test Base.Checked.sub_with_overflow(FD{Int8,2}(1), FD{Int8,2}(-1)) == (FD{Int8,2}(-0.56), true) - @test Base.Checked.sub_with_overflow(1, FD{Int8,2}(-1)) == (FD{Int8,2}(-0.56), true) @test Base.Checked.sub_with_overflow(FD{Int8,2}(-1), FD{Int8,2}(0.4)) == (FD{Int8,2}(1.16), true) - @test Base.Checked.mul_with_overflow(FD{Int8,2}(1.2), FD{Int8,2}(1.2)) == (FD{Int8,2}(-1.12), true) - @test Base.Checked.mul_with_overflow(FD{Int8,1}(12), 2) == (FD{Int8,1}(-1.6), true) - @test Base.Checked.mul_with_overflow(FD{Int8,0}(120), 2) == (FD{Int8,0}(-16), true) - @test Base.Checked.mul_with_overflow(120, FD{Int8,0}(2)) == (FD{Int8,0}(-16), true) @testset "with_overflow math corner cases" begin @testset for I in (Int128, UInt128, Int8, UInt8), f in (0,2) @@ -798,57 +792,42 @@ end @test Base.Checked.add_with_overflow(typemax(T), eps(T)) == (typemax(T) + eps(T), true) issigned(I) && @test Base.Checked.add_with_overflow(typemin(T), -eps(T)) == (typemin(T) + -eps(T), true) - @test Base.Checked.add_with_overflow(typemax(T), 1) == (typemax(T) + 1, true) - @test Base.Checked.add_with_overflow(1, typemax(T)) == (typemax(T) + 1, true) + @test Base.Checked.add_with_overflow(typemax(T), T(1)) == (typemax(T) + 1, true) + @test Base.Checked.add_with_overflow(T(1), typemax(T)) == (typemax(T) + 1, true) @test Base.Checked.sub_with_overflow(typemin(T), eps(T)) == (typemin(T) - eps(T), true) issigned(I) && @test Base.Checked.sub_with_overflow(typemax(T), -eps(T)) == (typemax(T) - -eps(T), true) - @test Base.Checked.sub_with_overflow(typemin(T), 1) == (typemin(T) - 1, true) + @test Base.Checked.sub_with_overflow(typemin(T), T(1)) == (typemin(T) - 1, true) if issigned(I) && 2.0 <= typemax(T) - @test Base.Checked.sub_with_overflow(-2, typemax(T)) == (-2 -typemax(T), true) + @test Base.Checked.sub_with_overflow(T(-2), typemax(T)) == (-2 -typemax(T), true) end @test Base.Checked.mul_with_overflow(typemax(T), typemax(T)) == (typemax(T) * typemax(T), true) issigned(I) && @test Base.Checked.mul_with_overflow(typemin(T), typemax(T)) == (typemin(T) * typemax(T), true) if 2.0 <= typemax(T) - @test Base.Checked.mul_with_overflow(typemax(T), 2) == (typemax(T) * 2, true) - @test Base.Checked.mul_with_overflow(2, typemax(T)) == (2 * typemax(T), true) - issigned(I) && @test Base.Checked.mul_with_overflow(typemin(T), 2) == (typemin(T) * 2, true) - issigned(I) && @test Base.Checked.mul_with_overflow(2, typemin(T)) == (2 * typemin(T), true) + @test Base.Checked.mul_with_overflow(typemax(T), T(2)) == (typemax(T) * 2, true) + @test Base.Checked.mul_with_overflow(T(2), typemax(T)) == (2 * typemax(T), true) + issigned(I) && @test Base.Checked.mul_with_overflow(typemin(T), T(2)) == (typemin(T) * 2, true) + issigned(I) && @test Base.Checked.mul_with_overflow(T(2), typemin(T)) == (2 * typemin(T), true) end end end - @testset "with_overflow math promotions" begin - x = FD{Int8,1}(1) - y = FD{Int64,1}(2) - @testset for op in ( - Base.Checked.add_with_overflow, Base.Checked.sub_with_overflow, - Base.Checked.mul_with_overflow, - ) - @test op(x, y) === op(FD{Int64,1}(1), y) - @test op(y, x) === op(y, FD{Int64,1}(1)) - - @test op(x, 2) === op(x, FD{Int8,1}(2)) - @test op(2, x) === op(FD{Int8,1}(2), x) - end - end - @testset "non-overflowing with_overflow math" begin - @test Base.Checked.add_with_overflow(1, FD{Int8,1}(1.1)) == (FD{Int8,1}(2.1), false) - @test Base.Checked.add_with_overflow(FD{Int8,1}(1.1), 1) == (FD{Int8,1}(2.1), false) + @test Base.Checked.add_with_overflow(FD{Int8,1}(1), FD{Int8,1}(1.1)) == (FD{Int8,1}(2.1), false) + @test Base.Checked.add_with_overflow(FD{Int8,1}(1.1), FD{Int8,1}(1)) == (FD{Int8,1}(2.1), false) @test Base.Checked.add_with_overflow(FD{Int64,8}(30.123), FD{Int64,8}(30)) == (FD{Int64,8}(60.123), false) - @test Base.Checked.add_with_overflow(FD{Int64,8}(30.123), FD{Int64,5}(30)) == (FD{Int64,8}(60.123), false) + @test Base.Checked.add_with_overflow(FD{Int64,8}(30.123), FD{Int64,8}(-50)) == (FD{Int64,8}(-19.877), false) - @test Base.Checked.sub_with_overflow(3, FD{Int16,2}(2.5)) == (FD{Int16,1}(0.5), false) - @test Base.Checked.sub_with_overflow(FD{Int16,2}(2.5), 3) == (FD{Int16,1}(-0.5), false) + @test Base.Checked.sub_with_overflow(FD{Int16,2}(3), FD{Int16,2}(2.5)) == (FD{Int16,1}(0.5), false) + @test Base.Checked.sub_with_overflow(FD{Int16,2}(2.5), FD{Int16,2}(3)) == (FD{Int16,1}(-0.5), false) @test Base.Checked.sub_with_overflow(FD{Int32,4}(10.11), FD{Int32,4}(2)) == (FD{Int32,4}(8.11), false) - @test Base.Checked.sub_with_overflow(FD{Int32,4}(10.11), FD{Int32,3}(2)) == (FD{Int32,4}(8.11), false) + @test Base.Checked.sub_with_overflow(FD{Int32,4}(10.11), FD{Int32,4}(-2)) == (FD{Int32,4}(12.11), false) - @test Base.Checked.mul_with_overflow(4, FD{Int64,6}(2.22)) == (FD{Int64,6}(8.88), false) - @test Base.Checked.mul_with_overflow(FD{Int64,6}(2.22), 4) == (FD{Int64,6}(8.88), false) + @test Base.Checked.mul_with_overflow(FD{Int64,6}(4), FD{Int64,6}(2.22)) == (FD{Int64,6}(8.88), false) + @test Base.Checked.mul_with_overflow(FD{Int64,6}(2.22), FD{Int64,6}(4)) == (FD{Int64,6}(8.88), false) @test Base.Checked.mul_with_overflow(FD{Int128,14}(10), FD{Int128,14}(20.1)) == (FD{Int128,14}(201), false) - @test Base.Checked.mul_with_overflow(FD{Int128,30}(10.11), FD{Int128,0}(1)) == (FD{Int128,30}(10.11), false) + @test Base.Checked.mul_with_overflow(FD{Int128,30}(10.1), FD{Int128,30}(1)) == (FD{Int128,30}(10.1), false) end end From d5ef6e6a0906f2a18ae7d9d819de55003c230ce5 Mon Sep 17 00:00:00 2001 From: Mohamad Barbar Date: Wed, 29 Jan 2025 02:28:35 +1100 Subject: [PATCH 07/19] Bump to 0.6 --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index f27746d..38bff96 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "FixedPointDecimals" uuid = "fb4d412d-6eee-574d-9565-ede6634db7b0" authors = ["Fengyang Wang ", "Curtis Vogt "] -version = "0.5.5" +version = "0.6" [deps] BitIntegers = "c3b6d118-76ef-56ca-8cc7-ebb389d030a1" From 5a0dfe5115f13af37122b440cc001a5ad5a3b2ae Mon Sep 17 00:00:00 2001 From: Nathan Daly Date: Tue, 28 Jan 2025 10:36:08 -0700 Subject: [PATCH 08/19] Update Project.toml --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 38bff96..6a27190 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "FixedPointDecimals" uuid = "fb4d412d-6eee-574d-9565-ede6634db7b0" authors = ["Fengyang Wang ", "Curtis Vogt "] -version = "0.6" +version = "0.6.0" [deps] BitIntegers = "c3b6d118-76ef-56ca-8cc7-ebb389d030a1" From 1399e4f238b3fc0f3aa1d75b5eedd3949ab5f3bc Mon Sep 17 00:00:00 2001 From: Mohamad Barbar Date: Fri, 7 Feb 2025 02:31:42 +1100 Subject: [PATCH 09/19] Add rdiv_with_overflow and fld_with_overflow. --- src/FixedPointDecimals.jl | 96 +++++++++++++++++++++++++++++++++------ test/FixedDecimal.jl | 29 ++++++++++++ 2 files changed, 110 insertions(+), 15 deletions(-) diff --git a/src/FixedPointDecimals.jl b/src/FixedPointDecimals.jl index 979d707..0a6d3fe 100644 --- a/src/FixedPointDecimals.jl +++ b/src/FixedPointDecimals.jl @@ -453,6 +453,78 @@ function div_with_overflow(x::FD{T,f}, y::FD{T,f}) where {T<:Integer,f} return (reinterpret(FD{T,f}, v), b) end +# Does not exist in Base.Checked, so just exists in this package. +@doc """ + FixedPointDecimals.fld_with_overflow(x::FD, y::FD)::Tuple{FD,Bool} + +Calculates the largest integer less than or equal to `x / y`, checking for overflow errors +where applicable, returning the result and a boolean indicating whether overflow occured. +Throws a DivideError on divide-by-zero. + +The overflow protection may impose a perceptible performance penalty. + +See also: +- `Base.checked_fld`. +""" +function fld_with_overflow(x::FD{T,f}, y::FD{T,f}) where {T<:Integer,f} + C = coefficient(FD{T, f}) + # This case will break the fld call below. This can only happen when f is 0 as y.i + # cannot be -1 otherwise. + if x.i == typemin(T) && y.i == -1 + # To fld and overflow means reaching the max and adding 1, so typemin (x). + return (x, true) + end + # Note: The fld() will already throw for divide-by-zero, that's not an overflow. + v, b = Base.Checked.mul_with_overflow(C, fld(x.i, y.i)) + return (reinterpret(FD{T, f}, v), b) +end + +""" + FixedPointDecimals.rdiv_with_overflow(x::FD, y::FD)::Tuple{FD,Bool} + +Calculates `x / y`, checking for overflow errors where applicable, returning the result +and a boolean indicating whether overflow occured. Throws a DivideError on divide-by-zero. + +The overflow protection may impose a perceptible performance penalty. + +See also: +- `Base.checked_rdiv`. +""" +function rdiv_with_overflow(x::FD{T,f}, y::FD{T,f}) where {T<:Integer,f} + powt = coefficient(FD{T, f}) + # No multiplication can reach the typemax/typemin of a wider type, thus no typemin / -1. + quotient, remainder = fldmod(_widemul(x.i, powt), y.i) + # quotient is necessarily not typemax/typemin. x.i * powt cannot reach typemax/typemin + # of the widened type and y.i is an integer. Thus the following call cannot overflow. + v = _round_to_nearest(quotient, remainder, y.i) + return (reinterpret(FD{T,f}, rem(v, T)), v < typemin(T) || v > typemax(T)) +end + +# These functions allow us to perform division with integers outside of the range of the +# FixedDecimal. +function rdiv_with_overflow(x::Integer, y::FD{T, f}) where {T<:Integer, f} + powt = coefficient(FD{T, f}) + powtsq = _widemul(powt, powt) + # No multiplication can reach the typemax/typemin of a wider type, thus no typemin / -1. + quotient, remainder = fldmod(_widemul(x, powtsq), y.i) + # Same deal as previous overload as to why this will not overload. Note that all + # multiplication operations were widemuls. + v = _round_to_nearest(quotient, remainder, y.i) + return (reinterpret(FD{T,f}, rem(v, T)), v < typemin(T) || v > typemax(T)) +end +function rdiv_with_overflow(x::FD{T, f}, y::Integer) where {T<:Integer, f} + if T <: Signed && x.i == typemin(T) && y == -1 + # typemin / -1 for signed integers wraps, giving typemin (x) again. + return (x, true) + end + + quotient, remainder = fldmod(x.i, y) + # It is impossible for both the quotient to be typemax/typemin AND remainder to be + # non-zero because y is an integer. Thus the following call cannot overflow. + v = _round_to_nearest(quotient, remainder, y) + return (reinterpret(FD{T, f}, v), false) +end + Base.checked_add(x::FD, y::FD) = Base.checked_add(promote(x, y)...) Base.checked_sub(x::FD, y::FD) = Base.checked_sub(promote(x, y)...) Base.checked_mul(x::FD, y::FD) = Base.checked_mul(promote(x, y)...) @@ -546,28 +618,22 @@ See also: checked_rdiv(x::FD, y::FD) = checked_rdiv(promote(x, y)...) function checked_rdiv(x::FD{T,f}, y::FD{T,f}) where {T<:Integer,f} - powt = coefficient(FD{T, f}) - quotient, remainder = fldmod(_widemul(x.i, powt), y.i) - v = _round_to_nearest(quotient, remainder, y.i) - typemin(T) <= v <= typemax(T) || Base.Checked.throw_overflowerr_binaryop(:/, x, y) - return reinterpret(FD{T, f}, v) + (z, b) = rdiv_with_overflow(x, y) + b && Base.Checked.throw_overflowerr_binaryop(:/, x, y) + return z end # These functions allow us to perform division with integers outside of the range of the # FixedDecimal. function checked_rdiv(x::Integer, y::FD{T, f}) where {T<:Integer, f} - powt = coefficient(FD{T, f}) - powtsq = _widemul(powt, powt) - quotient, remainder = fldmod(_widemul(x, powtsq), y.i) - v = _round_to_nearest(quotient, remainder, y.i) - typemin(T) <= v <= typemax(T) || Base.Checked.throw_overflowerr_binaryop(:/, x, y) - reinterpret(FD{T, f}, v) + (z, b) = rdiv_with_overflow(x, y) + b && Base.Checked.throw_overflowerr_binaryop(:/, x, y) + return z end function checked_rdiv(x::FD{T, f}, y::Integer) where {T<:Integer, f} - quotient, remainder = fldmod(x.i, y) - v = _round_to_nearest(quotient, remainder, y) - typemin(T) <= v <= typemax(T) || Base.Checked.throw_overflowerr_binaryop(:/, x, y) - reinterpret(FD{T, f}, v) + (z, b) = rdiv_with_overflow(x, y) + b && Base.Checked.throw_overflowerr_binaryop(:/, x, y) + return z end diff --git a/test/FixedDecimal.jl b/test/FixedDecimal.jl index 3fd4138..038be1d 100644 --- a/test/FixedDecimal.jl +++ b/test/FixedDecimal.jl @@ -777,6 +777,8 @@ end end @testset "limits: with_overflow math" begin + using FixedPointDecimals: rdiv_with_overflow, fld_with_overflow + # Easy to reason about cases of overflow: @test Base.Checked.add_with_overflow(FD{Int8,2}(1), FD{Int8,2}(1)) == (FD{Int8,2}(-0.56), true) @test Base.Checked.add_with_overflow(FD{Int8,2}(1), FD{Int8,2}(1)) == (FD{Int8,2}(-0.56), true) @@ -789,6 +791,16 @@ end @test div_with_overflow(typemin(FD{Int32,0}), FD{Int32,0}(-1)) == (typemin(FD{Int32,0}), true) @test div_with_overflow(FD{Int16,1}(1639), FD{Int16,1}(0.5)) == (FD{Int16,1}(-3275.6), true) + @test rdiv_with_overflow(Int8(1), FD{Int8,2}(0.7)) == (FD{Int8,2}(-1.13), true) + @test rdiv_with_overflow(FD{Int16,2}(165), FD{Int16,2}(0.5)) == (FD{Int16,2}(-325.36), true) + @test rdiv_with_overflow(FD{Int16,2}(-165), FD{Int16,2}(0.5)) == (FD{Int16,2}(325.36), true) + @test rdiv_with_overflow(typemin(FD{Int64,8}), Int32(-1)) == (typemin(FD{Int64,8}), true) + @test rdiv_with_overflow(typemin(FD{Int64,0}), FD{Int64,0}(-1)) == (typemin(FD{Int64,0}), true) + + @test fld_with_overflow(FD{Int8,2}(-1), FD{Int8,2}(0.9)) == (FD{Int8,2}(0.56), true) + @test fld_with_overflow(typemin(FD{Int64,0}), FD{Int64,0}(-1)) == (typemin(FD{Int64,0}), true) + @test fld_with_overflow(FD{Int8,1}(7), FD{Int8,1}(0.5)) == (FD{Int8,1}(-11.6), true) + @testset "with_overflow math corner cases" begin @testset for I in (Int128, UInt128, Int8, UInt8), f in (0,2) T = FD{I, f} @@ -823,7 +835,14 @@ end issigned(I) && @test_throws DivideError div_with_overflow(typemax(T), T(0)) issigned(I) && @test_throws DivideError div_with_overflow(typemin(T), T(0)) issigned(I) && @test div_with_overflow(typemin(T), -eps(T))[2] + + @test fld_with_overflow(typemax(T), eps(T))[2] + issigned(I) && @test fld_with_overflow(typemin(T), eps(T))[2] + issigned(I) && @test fld_with_overflow(typemax(T), -eps(T))[2] end + + @test_throws DivideError rdiv_with_overflow(typemax(T), T(0)) + @test_throws DivideError rdiv_with_overflow(typemin(T), T(0)) end end @@ -848,6 +867,16 @@ end @test div_with_overflow(FD{Int128,14}(10), FD{Int128,14}(20.1)) == (FD{Int128,14}(0), false) @test div_with_overflow(FD{Int128,30}(10.1), FD{Int128,30}(1)) == (FD{Int128,30}(10), false) @test div_with_overflow(typemin(FD{Int32,8}(1)), FD{Int32,8}(-1)) == (21, false) + + @test rdiv_with_overflow(Int8(1), FD{Int8,2}(0.8)) == (FD{Int8,2}(1.25), false) + @test rdiv_with_overflow(FD{Int64,8}(5), FD{Int64,8}(2)) == (FD{Int64,8}(2.5), false) + @test rdiv_with_overflow(FD{Int64,8}(5), FD{Int64,8}(0.5)) == (FD{Int64,8}(10), false) + @test rdiv_with_overflow(FD{Int128,0}(20000), Int32(5000)) == (FD{Int128,0}(4), false) + + @test fld_with_overflow(typemax(FD{Int128,38}), FD{Int128,38}(1)) == (FD{Int128,38}(1), false) + @test fld_with_overflow(FD{Int64,8}(20.5), FD{Int64,8}(2.1)) == (FD{Int64,8}(9), false) + @test fld_with_overflow(FD{Int8,0}(-5), FD{Int8,0}(-1)) == (FD{Int8,0}(5), false) + @test fld_with_overflow(FD{Int8,2}(0.99), FD{Int8,2}(0.5)) == (FD{Int8,2}(1), false) end end From 564d84187fb81542a4f33745cba455e2ceb68a52 Mon Sep 17 00:00:00 2001 From: Mohamad Barbar Date: Fri, 7 Feb 2025 02:33:03 +1100 Subject: [PATCH 10/19] Add more DIvideError tests. --- test/FixedDecimal.jl | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/test/FixedDecimal.jl b/test/FixedDecimal.jl index 038be1d..cfb2ddf 100644 --- a/test/FixedDecimal.jl +++ b/test/FixedDecimal.jl @@ -843,6 +843,13 @@ end @test_throws DivideError rdiv_with_overflow(typemax(T), T(0)) @test_throws DivideError rdiv_with_overflow(typemin(T), T(0)) + @test_throws DivideError rdiv_with_overflow(eps(T), T(0)) + @test_throws DivideError rdiv_with_overflow(-eps(T), T(0)) + + @test_throws DivideError fld_with_overflow(typemax(T), T(0)) + @test_throws DivideError fld_with_overflow(typemin(T), T(0)) + @test_throws DivideError fld_with_overflow(eps(T), T(0)) + @test_throws DivideError fld_with_overflow(-eps(T), T(0)) end end From 3ba8bde9ded6ec6eb5905cb50f00bd9945cf50f9 Mon Sep 17 00:00:00 2001 From: Mohamad Barbar Date: Fri, 7 Feb 2025 02:39:04 +1100 Subject: [PATCH 11/19] Only on Signed. --- src/FixedPointDecimals.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/FixedPointDecimals.jl b/src/FixedPointDecimals.jl index dabdeb8..4c596e4 100644 --- a/src/FixedPointDecimals.jl +++ b/src/FixedPointDecimals.jl @@ -471,7 +471,7 @@ function fld_with_overflow(x::FD{T,f}, y::FD{T,f}) where {T<:Integer,f} C = coefficient(FD{T, f}) # This case will break the fld call below. This can only happen when f is 0 as y.i # cannot be -1 otherwise. - if x.i == typemin(T) && y.i == -1 + if T <: Signed && x.i == typemin(T) && y.i == -1 # To fld and overflow means reaching the max and adding 1, so typemin (x). return (x, true) end From 5f0c0b6a2ebcf6affbae4eb4c3a1dd8dcc2871d8 Mon Sep 17 00:00:00 2001 From: Mohamad Barbar Date: Thu, 13 Feb 2025 01:42:12 +1100 Subject: [PATCH 12/19] Add round_with_overflow, ceil_with_overflow, floor_with_overflow. --- src/FixedPointDecimals.jl | 61 +++++++++++ test/FixedDecimal.jl | 213 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 274 insertions(+) diff --git a/src/FixedPointDecimals.jl b/src/FixedPointDecimals.jl index 4c596e4..9911f5c 100644 --- a/src/FixedPointDecimals.jl +++ b/src/FixedPointDecimals.jl @@ -526,6 +526,67 @@ function rdiv_with_overflow(x::FD{T, f}, y::Integer) where {T<:Integer, f} return (reinterpret(FD{T, f}, v), false) end +# Does not exist in Base.Checked, so just exists in this package. +@doc """ + FixedPointDecimals.ceil_with_overflow(x::FD)::Tuple{FD,Bool} + +Calculates the nearest integral value of the same type as x that is greater than or equal +to x, returning it and a boolean indicating whether overflow has occurred. + +The overflow protection may impose a perceptible performance penalty. +""" +function ceil_with_overflow(x::FD{T,f}) where {T<:Integer,f} + powt = coefficient(FD{T, f}) + quotient, remainder = fldmodinline(x.i, powt) + return if remainder > 0 + # Could overflow when powt is 1 (f is 0) and x/x.i is typemax. + v, add_overflowed = Base.Checked.add_with_overflow(quotient, one(quotient)) + # Could overflow when x is close to typemax (max quotient) independent of f. + backing, mul_overflowed = Base.Checked.mul_with_overflow(v, powt) + (reinterpret(FD{T, f}, backing), add_overflowed || mul_overflowed) + else + (FD{T, f}(quotient), false) + end +end + +# Does not exist in Base.Checked, so just exists in this package. +@doc """ + FixedPointDecimals.floor_with_overflow(x::FD)::Tuple{FD,Bool} + +Calculates the nearest integral value of the same type as x that is less than or equal +to x, returning it and a boolean indicating whether overflow has occurred. + +The overflow protection may impose a perceptible performance penalty. +""" +function floor_with_overflow(x::FD{T, f}) where {T, f} + powt = coefficient(FD{T, f}) + # Won't underflow, powt is an integer. + quotient = fld(x.i, powt) + # When we convert it back to the backing format it might though. Occurs when + # the integer part of x is at its maximum. + backing, overflowed = Base.Checked.mul_with_overflow(quotient, powt) + return (reinterpret(FD{T, f}, backing), overflowed) +end + +round_with_overflow(fd::FD, ::RoundingMode{:Up}) = ceil_with_overflow(fd) +round_with_overflow(fd::FD, ::RoundingMode{:Down}) = floor_with_overflow(fd) +# trunc cannot overflow. +round_with_overflow(fd::FD, ::RoundingMode{:ToZero}) = (trunc(fd), false) +function round_with_overflow( + x::FD{T, f}, + m::Union{ + RoundingMode{:Nearest}, + RoundingMode{:NearestTiesUp}, + RoundingMode{:NearestTiesAway} + }=RoundNearest, +) where {T, f} + powt = coefficient(FD{T, f}) + quotient, remainder = fldmodinline(x.i, powt) + v = _round_to_nearest(quotient, remainder, powt, m) + backing, overflowed = Base.Checked.mul_with_overflow(v, powt) + (reinterpret(FD{T, f}, backing), overflowed) +end + Base.checked_add(x::FD, y::FD) = Base.checked_add(promote(x, y)...) Base.checked_sub(x::FD, y::FD) = Base.checked_sub(promote(x, y)...) Base.checked_mul(x::FD, y::FD) = Base.checked_mul(promote(x, y)...) diff --git a/test/FixedDecimal.jl b/test/FixedDecimal.jl index cfb2ddf..17f17e2 100644 --- a/test/FixedDecimal.jl +++ b/test/FixedDecimal.jl @@ -1268,6 +1268,120 @@ end end end +@testset "round_with_overflow" begin + using FixedPointDecimals: round_with_overflow + + # Is alias for `ceil`. + @testset "up" begin + @test round_with_overflow(FD2(-0.51), RoundUp) === (FD2(0), false) + @test round_with_overflow(FD2(-0.50), RoundUp) === (FD2(0), false) + @test round_with_overflow(FD2(-0.49), RoundUp) === (FD2(0), false) + @test round_with_overflow(FD2(0.50), RoundUp) === (FD2(1), false) + @test round_with_overflow(FD2(0.51), RoundUp) === (FD2(1), false) + @test round_with_overflow(FD2(1.50), RoundUp) === (FD2(2), false) + @test round_with_overflow(typemin(FD2), RoundUp) === + (parse(FD2, "-92233720368547758"), false) + + @testset "overflowing" begin + @test round_with_overflow(typemax(FD2), RoundUp) === + (parse(FD2, "-92233720368547757.16"), true) + @test round_with_overflow(parse(FD2, "92233720368547758.01"), RoundUp) === + (parse(FD2, "-92233720368547757.16"), true) + end + end + + # Is alias for `floor`. + @testset "down" begin + @test round_with_overflow(FD2(-0.51), RoundDown) === (FD2(-1), false) + @test round_with_overflow(FD2(-0.50), RoundDown) === (FD2(-1), false) + @test round_with_overflow(FD2(-0.49), RoundDown) === (FD2(-1), false) + @test round_with_overflow(FD2(0.50), RoundDown) === (FD2(0), false) + @test round_with_overflow(FD2(0.51), RoundDown) === (FD2(0), false) + @test round_with_overflow(FD2(1.50), RoundDown) === (FD2(1), false) + @test round_with_overflow(typemax(FD2), RoundDown) === + (parse(FD2, "92233720368547758"), false) + + @testset "overflowing" begin + @test round_with_overflow(typemin(FD2), RoundDown) === + (parse(FD2, "92233720368547757.16"), true) + @test round_with_overflow(parse(FD2, "-92233720368547758.01"), RoundDown) === + (parse(FD2, "92233720368547757.16"), true) + end + end + + # Is alias for `trunc`. + @testset "to zero" begin + @test round_with_overflow(FD2(-0.51), RoundToZero) === (FD2(0), false) + @test round_with_overflow(FD2(-0.50), RoundToZero) === (FD2(0), false) + @test round_with_overflow(FD2(-0.49), RoundToZero) === (FD2(0), false) + @test round_with_overflow(FD2(0.50), RoundToZero) === (FD2(0), false) + @test round_with_overflow(FD2(0.51), RoundToZero) === (FD2(0), false) + @test round_with_overflow(FD2(1.50), RoundToZero) === (FD2(1), false) + + @test round_with_overflow(typemin(FD2), RoundToZero) === + (parse(FD2, "-92233720368547758"), false) + @test round_with_overflow(typemax(FD2), RoundToZero) === + (parse(FD2, "92233720368547758"), false) + + # Cannot overflow. + end + + @testset "tie away" begin + @test round_with_overflow(FD2(-0.51), RoundNearestTiesAway) === (FD2(-1), false) + @test round_with_overflow(FD2(-0.50), RoundNearestTiesAway) === (FD2(-1), false) + @test round_with_overflow(FD2(-0.49), RoundNearestTiesAway) === (FD2(0), false) + @test round_with_overflow(FD2(0.50), RoundNearestTiesAway) === (FD2(1), false) + @test round_with_overflow(FD2(0.51), RoundNearestTiesAway) === (FD2(1), false) + @test round_with_overflow(FD2(1.50), RoundNearestTiesAway) === (FD2(2), false) + + @test round_with_overflow(typemin(FD2), RoundNearestTiesAway) === + (parse(FD2, "-92233720368547758"), false) + @test round_with_overflow(typemax(FD2), RoundNearestTiesAway) === + (parse(FD2, "92233720368547758"), false) + + @testset "overflowing" begin + # For max, FD2 has fractional .07 so use FD3 which has .807. + @test round_with_overflow(typemin(FD3), RoundNearestTiesAway) === + (parse(FD3, "9223372036854775.616"), true) + @test round_with_overflow(typemax(FD3), RoundNearestTiesAway) === + (parse(FD3, "-9223372036854775.616"), true) + + @test round_with_overflow(parse(FD3, "9223372036854775.5"), RoundNearestTiesAway) === + (parse(FD3, "-9223372036854775.616"), true) + @test round_with_overflow(parse(FD3, "-9223372036854775.5"), RoundNearestTiesAway) === + (parse(FD3, "9223372036854775.616"), true) + end + end + + @testset "tie up" begin + @test round_with_overflow(FD2(-0.51), RoundNearestTiesUp) === (FD2(-1), false) + @test round_with_overflow(FD2(-0.50), RoundNearestTiesUp) === (FD2(0), false) + @test round_with_overflow(FD2(-0.49), RoundNearestTiesUp) === (FD2(0), false) + @test round_with_overflow(FD2(0.50), RoundNearestTiesUp) === (FD2(1), false) + @test round_with_overflow(FD2(0.51), RoundNearestTiesUp) === (FD2(1), false) + @test round_with_overflow(FD2(1.50), RoundNearestTiesUp) === (FD2(2), false) + + @test round_with_overflow(typemin(FD2), RoundNearestTiesUp) === + (parse(FD2, "-92233720368547758"), false) + @test round_with_overflow(typemax(FD2), RoundNearestTiesUp) === + (parse(FD2, "92233720368547758"), false) + + # For max, FD2 has fractional .07 so use FD3 which has .807. + @test round_with_overflow(parse(FD3, "-9223372036854775.5"), RoundNearestTiesUp) === + (FD3(-9223372036854775), false) + + @testset "overflowing" begin + @test round_with_overflow(typemin(FD3), RoundNearestTiesUp) === + (parse(FD3, "9223372036854775.616"), true) + @test round_with_overflow(typemax(FD3), RoundNearestTiesUp) === + (parse(FD3, "-9223372036854775.616"), true) + + @test round_with_overflow(parse(FD3, "9223372036854775.5"), RoundNearestTiesUp) === + (parse(FD3, "-9223372036854775.616"), true) + end + end +end + @testset "trunc" begin @test trunc(Int, FD2(0.99)) === 0 @test trunc(Int, FD2(-0.99)) === 0 @@ -1416,6 +1530,105 @@ epsi(::Type{T}) where T = eps(T) end end +@testset "floor_with_overflow" begin + using FixedPointDecimals: floor_with_overflow + + @testset "non-overflowing" begin + @test floor_with_overflow(FD{Int8,2}(1.02)) == (FD{Int8,2}(1), false) + @test floor_with_overflow(FD{Int8,2}(-0.02)) == (FD{Int8,2}(-1), false) + @test floor_with_overflow(FD{Int8,2}(-1)) == (FD{Int8,2}(-1), false) + + @test floor_with_overflow(FD{Int16,1}(5.2)) == (FD{Int16,1}(5), false) + @test floor_with_overflow(FD{Int16,1}(-5.2)) == (FD{Int16,1}(-6), false) + + @test floor_with_overflow(typemax(FD{Int32,0})) == (typemax(FD{Int32,0}), false) + @test floor_with_overflow(typemin(FD{Int32,0})) == (typemin(FD{Int32,0}), false) + + @test floor_with_overflow(FD{Int64,8}(40.054672)) == (FD{Int64,8}(40), false) + @test floor_with_overflow(FD{Int64,8}(-40.054672)) == (FD{Int64,8}(-41), false) + @test floor_with_overflow(FD{Int64,8}(-92233720368)) == + (FD{Int64,8}(-92233720368), false) + + @test floor_with_overflow(typemax(FD{Int128,18})) == + (FD{Int128,18}(170141183460469231731), false) + @test floor_with_overflow(FD{Int128,18}(-400.0546798232)) == + (FD{Int128,18}(-401), false) + end + + @testset "overflowing" begin + @test floor_with_overflow(typemin(FD{Int8,2})) == (FD{Int8,2}(0.56), true) + @test floor_with_overflow(FD{Int8,2}(-1.02)) == (FD{Int8,2}(0.56), true) + + @test floor_with_overflow(typemin(FD{Int16,3})) == (FD{Int16,3}(32.536), true) + @test floor_with_overflow(FD{Int16,3}(-32.111)) == (FD{Int16,3}(32.536), true) + + @test floor_with_overflow(typemin(FD{Int32,1})) == (FD{Int32,1}(214748364.6), true) + @test floor_with_overflow(FD{Int32,1}(-214748364.7)) == + (FD{Int32,1}(214748364.6), true) + + @test floor_with_overflow(typemin(FD{Int64,8})) == + (parse(FD{Int64,8}, "92233720368.09551616"), true) + @test floor_with_overflow(FD{Int64,8}(-92233720368.5)) == + (parse(FD{Int64,8}, "92233720368.09551616"), true) + + @test floor_with_overflow(typemin(FD{Int128,2})) == + (parse(FD{Int128,2}, "1701411834604692317316873037158841056.56"), true) + @test floor_with_overflow(parse(FD{Int128,2}, "-1701411834604692317316873037158841057.27")) == + (parse(FD{Int128,2}, "1701411834604692317316873037158841056.56"), true) + end +end + +@testset "ceil_with_overflow" begin + using FixedPointDecimals: ceil_with_overflow + + @testset "non-overflowing" begin + @test ceil_with_overflow(FD{Int8,2}(-1.02)) == (FD{Int8,2}(-1), false) + @test ceil_with_overflow(FD{Int8,2}(-0.02)) == (FD{Int8,2}(0), false) + @test ceil_with_overflow(FD{Int8,2}(0.49)) == (FD{Int8,2}(1), false) + @test ceil_with_overflow(FD{Int8,2}(1)) == (FD{Int8,2}(1), false) + + @test ceil_with_overflow(FD{Int16,1}(5.2)) == (FD{Int16,1}(6), false) + @test ceil_with_overflow(FD{Int16,1}(-5.2)) == (FD{Int16,1}(-5), false) + + @test ceil_with_overflow(typemax(FD{Int32,0})) == (typemax(FD{Int32,0}), false) + @test ceil_with_overflow(typemin(FD{Int32,0})) == (typemin(FD{Int32,0}), false) + + @test ceil_with_overflow(FD{Int64,8}(40.054672)) == (FD{Int64,8}(41), false) + @test ceil_with_overflow(FD{Int64,8}(-40.054672)) == (FD{Int64,8}(-40), false) + @test ceil_with_overflow(FD{Int64,8}(-92233720368)) == + (FD{Int64,8}(-92233720368), false) + @test ceil_with_overflow(FD{Int64,8}(92233720368)) == + (FD{Int64,8}(92233720368), false) + + @test ceil_with_overflow(typemin(FD{Int128,18})) == + (FD{Int128,18}(-170141183460469231731), false) + @test ceil_with_overflow(FD{Int128,18}(-400.0546798232)) == + (FD{Int128,18}(-400), false) + end + + @testset "overflowing" begin + @test ceil_with_overflow(typemax(FD{Int8,2})) == (FD{Int8,2}(-0.56), true) + @test ceil_with_overflow(FD{Int8,2}(1.02)) == (FD{Int8,2}(-0.56), true) + + @test ceil_with_overflow(typemax(FD{Int16,3})) == (FD{Int16,3}(-32.536), true) + @test ceil_with_overflow(FD{Int16,3}(32.111)) == (FD{Int16,3}(-32.536), true) + + @test ceil_with_overflow(typemax(FD{Int32,1})) == (FD{Int32,1}(-214748364.6), true) + @test ceil_with_overflow(FD{Int32,1}(214748364.7)) == + (FD{Int32,1}(-214748364.6), true) + + @test ceil_with_overflow(typemax(FD{Int64,8})) == + (parse(FD{Int64,8}, "-92233720368.09551616"), true) + @test ceil_with_overflow(FD{Int64,8}(92233720368.5)) == + (parse(FD{Int64,8}, "-92233720368.09551616"), true) + + @test ceil_with_overflow(typemax(FD{Int128,2})) == + (parse(FD{Int128,2}, "-1701411834604692317316873037158841056.56"), true) + @test ceil_with_overflow(parse(FD{Int128,2}, "1701411834604692317316873037158841057.27")) == + (parse(FD{Int128,2}, "-1701411834604692317316873037158841056.56"), true) + end +end + @testset "type stability" begin # Test that basic operations are type stable for all the basic integer types. fs = [0, 1, 2, 7, 16, 38] # To save time, don't test all possible combinations. From a59ebdf86d82b3043b1a15481aa48a753dfd1f1c Mon Sep 17 00:00:00 2001 From: Mohamad Barbar Date: Fri, 14 Feb 2025 15:58:31 +1100 Subject: [PATCH 13/19] Add signed check --- src/FixedPointDecimals.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/FixedPointDecimals.jl b/src/FixedPointDecimals.jl index 4c596e4..32341df 100644 --- a/src/FixedPointDecimals.jl +++ b/src/FixedPointDecimals.jl @@ -445,7 +445,7 @@ overflow/underflow did in fact happen. Throws a DivideError on divide-by-zero. function div_with_overflow(x::FD{T,f}, y::FD{T,f}) where {T<:Integer,f} C = coefficient(FD{T, f}) # This case will break the div call below. - if x.i == typemin(T) && y.i == -1 + if T <: Signed && x.i == typemin(T) && y.i == -1 # To perform the div and overflow means reaching the max and adding 1, so typemin. return (x, true) end From d0cdb98edd85fff14070e8afa2bfdc0f3f0e7acc Mon Sep 17 00:00:00 2001 From: Mohamad Barbar Date: Fri, 14 Feb 2025 16:00:44 +1100 Subject: [PATCH 14/19] Remove comment --- src/FixedPointDecimals.jl | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/FixedPointDecimals.jl b/src/FixedPointDecimals.jl index 32341df..1eda84a 100644 --- a/src/FixedPointDecimals.jl +++ b/src/FixedPointDecimals.jl @@ -444,7 +444,7 @@ overflow/underflow did in fact happen. Throws a DivideError on divide-by-zero. """ function div_with_overflow(x::FD{T,f}, y::FD{T,f}) where {T<:Integer,f} C = coefficient(FD{T, f}) - # This case will break the div call below. + # This case will break the fld call below. if T <: Signed && x.i == typemin(T) && y.i == -1 # To perform the div and overflow means reaching the max and adding 1, so typemin. return (x, true) @@ -469,8 +469,7 @@ See also: """ function fld_with_overflow(x::FD{T,f}, y::FD{T,f}) where {T<:Integer,f} C = coefficient(FD{T, f}) - # This case will break the fld call below. This can only happen when f is 0 as y.i - # cannot be -1 otherwise. + # This case will break the fld call below. if T <: Signed && x.i == typemin(T) && y.i == -1 # To fld and overflow means reaching the max and adding 1, so typemin (x). return (x, true) From 6976457751f50e94d1f60f082d6e523a19b8c0fd Mon Sep 17 00:00:00 2001 From: Mohamad Barbar Date: Sat, 15 Feb 2025 23:27:01 +1100 Subject: [PATCH 15/19] Address --- src/FixedPointDecimals.jl | 6 +++--- test/FixedDecimal.jl | 2 ++ 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/FixedPointDecimals.jl b/src/FixedPointDecimals.jl index 1eda84a..df844cf 100644 --- a/src/FixedPointDecimals.jl +++ b/src/FixedPointDecimals.jl @@ -445,7 +445,7 @@ overflow/underflow did in fact happen. Throws a DivideError on divide-by-zero. function div_with_overflow(x::FD{T,f}, y::FD{T,f}) where {T<:Integer,f} C = coefficient(FD{T, f}) # This case will break the fld call below. - if T <: Signed && x.i == typemin(T) && y.i == -1 + if y.i == -1 && T <: Signed && hasmethod(typemin, (Type{T},)) && x.i == typemin(T) # To perform the div and overflow means reaching the max and adding 1, so typemin. return (x, true) end @@ -470,7 +470,7 @@ See also: function fld_with_overflow(x::FD{T,f}, y::FD{T,f}) where {T<:Integer,f} C = coefficient(FD{T, f}) # This case will break the fld call below. - if T <: Signed && x.i == typemin(T) && y.i == -1 + if y.i == -1 && T <: Signed && hasmethod(typemin, (Type{T},)) && x.i == typemin(T) # To fld and overflow means reaching the max and adding 1, so typemin (x). return (x, true) end @@ -513,7 +513,7 @@ function rdiv_with_overflow(x::Integer, y::FD{T, f}) where {T<:Integer, f} return (reinterpret(FD{T,f}, rem(v, T)), v < typemin(T) || v > typemax(T)) end function rdiv_with_overflow(x::FD{T, f}, y::Integer) where {T<:Integer, f} - if T <: Signed && x.i == typemin(T) && y == -1 + if y == -1 && T <: Signed && hasmethod(typemin, (Type{T},)) && x.i == typemin(T) # typemin / -1 for signed integers wraps, giving typemin (x) again. return (x, true) end diff --git a/test/FixedDecimal.jl b/test/FixedDecimal.jl index cfb2ddf..949c08c 100644 --- a/test/FixedDecimal.jl +++ b/test/FixedDecimal.jl @@ -796,6 +796,7 @@ end @test rdiv_with_overflow(FD{Int16,2}(-165), FD{Int16,2}(0.5)) == (FD{Int16,2}(325.36), true) @test rdiv_with_overflow(typemin(FD{Int64,8}), Int32(-1)) == (typemin(FD{Int64,8}), true) @test rdiv_with_overflow(typemin(FD{Int64,0}), FD{Int64,0}(-1)) == (typemin(FD{Int64,0}), true) + @test rdiv_with_overflow(typemin(FD{Int8,2}), FD{Int8,2}(-1)) == (typemin(FD{Int8,2}), true) @test fld_with_overflow(FD{Int8,2}(-1), FD{Int8,2}(0.9)) == (FD{Int8,2}(0.56), true) @test fld_with_overflow(typemin(FD{Int64,0}), FD{Int64,0}(-1)) == (typemin(FD{Int64,0}), true) @@ -884,6 +885,7 @@ end @test fld_with_overflow(FD{Int64,8}(20.5), FD{Int64,8}(2.1)) == (FD{Int64,8}(9), false) @test fld_with_overflow(FD{Int8,0}(-5), FD{Int8,0}(-1)) == (FD{Int8,0}(5), false) @test fld_with_overflow(FD{Int8,2}(0.99), FD{Int8,2}(0.5)) == (FD{Int8,2}(1), false) + @test fld_with_overflow(typemin(FD{Int8,2}), FD{Int8,2}(-1)) == (FD{Int8,2}(1), false) end end From b2f0a82051ed78b2c48a4aa9f26e35d264d075f3 Mon Sep 17 00:00:00 2001 From: Nathan Daly Date: Mon, 17 Feb 2025 09:57:20 -0700 Subject: [PATCH 16/19] Apply suggestions from code review --- test/FixedDecimal.jl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/FixedDecimal.jl b/test/FixedDecimal.jl index 949c08c..b1d8528 100644 --- a/test/FixedDecimal.jl +++ b/test/FixedDecimal.jl @@ -797,10 +797,12 @@ end @test rdiv_with_overflow(typemin(FD{Int64,8}), Int32(-1)) == (typemin(FD{Int64,8}), true) @test rdiv_with_overflow(typemin(FD{Int64,0}), FD{Int64,0}(-1)) == (typemin(FD{Int64,0}), true) @test rdiv_with_overflow(typemin(FD{Int8,2}), FD{Int8,2}(-1)) == (typemin(FD{Int8,2}), true) + @test rdiv_with_overflow(typemin(FD{Int8,2}), FD{Int8,2}(-0.01)) == (FD{Int8,2}(0), true) @test fld_with_overflow(FD{Int8,2}(-1), FD{Int8,2}(0.9)) == (FD{Int8,2}(0.56), true) @test fld_with_overflow(typemin(FD{Int64,0}), FD{Int64,0}(-1)) == (typemin(FD{Int64,0}), true) @test fld_with_overflow(FD{Int8,1}(7), FD{Int8,1}(0.5)) == (FD{Int8,1}(-11.6), true) + @test FixedPointDecimals.fld_with_overflow(typemin(FD{Int8,2}), FD{Int8,2}(-0.01)) == (typemin(FD{Int8,2}), true) @testset "with_overflow math corner cases" begin @testset for I in (Int128, UInt128, Int8, UInt8), f in (0,2) From 933367b4202ae8b164c325b0b1ac1f0ab4c52843 Mon Sep 17 00:00:00 2001 From: Nathan Daly Date: Mon, 17 Feb 2025 10:39:28 -0700 Subject: [PATCH 17/19] bump to v0.6.2 --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 11f7b50..8a3875b 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "FixedPointDecimals" uuid = "fb4d412d-6eee-574d-9565-ede6634db7b0" authors = ["Fengyang Wang ", "Curtis Vogt "] -version = "0.6.1" +version = "0.6.2" [deps] BitIntegers = "c3b6d118-76ef-56ca-8cc7-ebb389d030a1" From d4a42435e4bc4664baf84f4a5536849265a7bb6b Mon Sep 17 00:00:00 2001 From: Mohamad Barbar Date: Tue, 18 Feb 2025 22:17:53 +1100 Subject: [PATCH 18/19] Explicitly 64 bit --- test/FixedDecimal.jl | 157 ++++++++++++++++++++++--------------------- 1 file changed, 80 insertions(+), 77 deletions(-) diff --git a/test/FixedDecimal.jl b/test/FixedDecimal.jl index a99d374..6b092ff 100644 --- a/test/FixedDecimal.jl +++ b/test/FixedDecimal.jl @@ -1275,113 +1275,116 @@ end @testset "round_with_overflow" begin using FixedPointDecimals: round_with_overflow + FD642 = FixedDecimal{Int64,2} + FD643 = FixedDecimal{Int64,3} + # Is alias for `ceil`. @testset "up" begin - @test round_with_overflow(FD2(-0.51), RoundUp) === (FD2(0), false) - @test round_with_overflow(FD2(-0.50), RoundUp) === (FD2(0), false) - @test round_with_overflow(FD2(-0.49), RoundUp) === (FD2(0), false) - @test round_with_overflow(FD2(0.50), RoundUp) === (FD2(1), false) - @test round_with_overflow(FD2(0.51), RoundUp) === (FD2(1), false) - @test round_with_overflow(FD2(1.50), RoundUp) === (FD2(2), false) - @test round_with_overflow(typemin(FD2), RoundUp) === - (parse(FD2, "-92233720368547758"), false) + @test round_with_overflow(FD642(-0.51), RoundUp) === (FD642(0), false) + @test round_with_overflow(FD642(-0.50), RoundUp) === (FD642(0), false) + @test round_with_overflow(FD642(-0.49), RoundUp) === (FD642(0), false) + @test round_with_overflow(FD642(0.50), RoundUp) === (FD642(1), false) + @test round_with_overflow(FD642(0.51), RoundUp) === (FD642(1), false) + @test round_with_overflow(FD642(1.50), RoundUp) === (FD642(2), false) + @test round_with_overflow(typemin(FD642), RoundUp) === + (parse(FD642, "-92233720368547758"), false) @testset "overflowing" begin - @test round_with_overflow(typemax(FD2), RoundUp) === - (parse(FD2, "-92233720368547757.16"), true) - @test round_with_overflow(parse(FD2, "92233720368547758.01"), RoundUp) === - (parse(FD2, "-92233720368547757.16"), true) + @test round_with_overflow(typemax(FD642), RoundUp) === + (parse(FD642, "-92233720368547757.16"), true) + @test round_with_overflow(parse(FD642, "92233720368547758.01"), RoundUp) === + (parse(FD642, "-92233720368547757.16"), true) end end # Is alias for `floor`. @testset "down" begin - @test round_with_overflow(FD2(-0.51), RoundDown) === (FD2(-1), false) - @test round_with_overflow(FD2(-0.50), RoundDown) === (FD2(-1), false) - @test round_with_overflow(FD2(-0.49), RoundDown) === (FD2(-1), false) - @test round_with_overflow(FD2(0.50), RoundDown) === (FD2(0), false) - @test round_with_overflow(FD2(0.51), RoundDown) === (FD2(0), false) - @test round_with_overflow(FD2(1.50), RoundDown) === (FD2(1), false) - @test round_with_overflow(typemax(FD2), RoundDown) === - (parse(FD2, "92233720368547758"), false) + @test round_with_overflow(FD642(-0.51), RoundDown) === (FD642(-1), false) + @test round_with_overflow(FD642(-0.50), RoundDown) === (FD642(-1), false) + @test round_with_overflow(FD642(-0.49), RoundDown) === (FD642(-1), false) + @test round_with_overflow(FD642(0.50), RoundDown) === (FD642(0), false) + @test round_with_overflow(FD642(0.51), RoundDown) === (FD642(0), false) + @test round_with_overflow(FD642(1.50), RoundDown) === (FD642(1), false) + @test round_with_overflow(typemax(FD642), RoundDown) === + (parse(FD642, "92233720368547758"), false) @testset "overflowing" begin - @test round_with_overflow(typemin(FD2), RoundDown) === - (parse(FD2, "92233720368547757.16"), true) - @test round_with_overflow(parse(FD2, "-92233720368547758.01"), RoundDown) === - (parse(FD2, "92233720368547757.16"), true) + @test round_with_overflow(typemin(FD642), RoundDown) === + (parse(FD642, "92233720368547757.16"), true) + @test round_with_overflow(parse(FD642, "-92233720368547758.01"), RoundDown) === + (parse(FD642, "92233720368547757.16"), true) end end # Is alias for `trunc`. @testset "to zero" begin - @test round_with_overflow(FD2(-0.51), RoundToZero) === (FD2(0), false) - @test round_with_overflow(FD2(-0.50), RoundToZero) === (FD2(0), false) - @test round_with_overflow(FD2(-0.49), RoundToZero) === (FD2(0), false) - @test round_with_overflow(FD2(0.50), RoundToZero) === (FD2(0), false) - @test round_with_overflow(FD2(0.51), RoundToZero) === (FD2(0), false) - @test round_with_overflow(FD2(1.50), RoundToZero) === (FD2(1), false) - - @test round_with_overflow(typemin(FD2), RoundToZero) === - (parse(FD2, "-92233720368547758"), false) - @test round_with_overflow(typemax(FD2), RoundToZero) === - (parse(FD2, "92233720368547758"), false) + @test round_with_overflow(FD642(-0.51), RoundToZero) === (FD642(0), false) + @test round_with_overflow(FD642(-0.50), RoundToZero) === (FD642(0), false) + @test round_with_overflow(FD642(-0.49), RoundToZero) === (FD642(0), false) + @test round_with_overflow(FD642(0.50), RoundToZero) === (FD642(0), false) + @test round_with_overflow(FD642(0.51), RoundToZero) === (FD642(0), false) + @test round_with_overflow(FD642(1.50), RoundToZero) === (FD642(1), false) + + @test round_with_overflow(typemin(FD642), RoundToZero) === + (parse(FD642, "-92233720368547758"), false) + @test round_with_overflow(typemax(FD642), RoundToZero) === + (parse(FD642, "92233720368547758"), false) # Cannot overflow. end @testset "tie away" begin - @test round_with_overflow(FD2(-0.51), RoundNearestTiesAway) === (FD2(-1), false) - @test round_with_overflow(FD2(-0.50), RoundNearestTiesAway) === (FD2(-1), false) - @test round_with_overflow(FD2(-0.49), RoundNearestTiesAway) === (FD2(0), false) - @test round_with_overflow(FD2(0.50), RoundNearestTiesAway) === (FD2(1), false) - @test round_with_overflow(FD2(0.51), RoundNearestTiesAway) === (FD2(1), false) - @test round_with_overflow(FD2(1.50), RoundNearestTiesAway) === (FD2(2), false) - - @test round_with_overflow(typemin(FD2), RoundNearestTiesAway) === - (parse(FD2, "-92233720368547758"), false) - @test round_with_overflow(typemax(FD2), RoundNearestTiesAway) === - (parse(FD2, "92233720368547758"), false) + @test round_with_overflow(FD642(-0.51), RoundNearestTiesAway) === (FD642(-1), false) + @test round_with_overflow(FD642(-0.50), RoundNearestTiesAway) === (FD642(-1), false) + @test round_with_overflow(FD642(-0.49), RoundNearestTiesAway) === (FD642(0), false) + @test round_with_overflow(FD642(0.50), RoundNearestTiesAway) === (FD642(1), false) + @test round_with_overflow(FD642(0.51), RoundNearestTiesAway) === (FD642(1), false) + @test round_with_overflow(FD642(1.50), RoundNearestTiesAway) === (FD642(2), false) + + @test round_with_overflow(typemin(FD642), RoundNearestTiesAway) === + (parse(FD642, "-92233720368547758"), false) + @test round_with_overflow(typemax(FD642), RoundNearestTiesAway) === + (parse(FD642, "92233720368547758"), false) @testset "overflowing" begin - # For max, FD2 has fractional .07 so use FD3 which has .807. - @test round_with_overflow(typemin(FD3), RoundNearestTiesAway) === - (parse(FD3, "9223372036854775.616"), true) - @test round_with_overflow(typemax(FD3), RoundNearestTiesAway) === - (parse(FD3, "-9223372036854775.616"), true) - - @test round_with_overflow(parse(FD3, "9223372036854775.5"), RoundNearestTiesAway) === - (parse(FD3, "-9223372036854775.616"), true) - @test round_with_overflow(parse(FD3, "-9223372036854775.5"), RoundNearestTiesAway) === - (parse(FD3, "9223372036854775.616"), true) + # For max, FD642 has fractional .07 so use FD643 which has .807. + @test round_with_overflow(typemin(FD643), RoundNearestTiesAway) === + (parse(FD643, "9223372036854775.616"), true) + @test round_with_overflow(typemax(FD643), RoundNearestTiesAway) === + (parse(FD643, "-9223372036854775.616"), true) + + @test round_with_overflow(parse(FD643, "9223372036854775.5"), RoundNearestTiesAway) === + (parse(FD643, "-9223372036854775.616"), true) + @test round_with_overflow(parse(FD643, "-9223372036854775.5"), RoundNearestTiesAway) === + (parse(FD643, "9223372036854775.616"), true) end end @testset "tie up" begin - @test round_with_overflow(FD2(-0.51), RoundNearestTiesUp) === (FD2(-1), false) - @test round_with_overflow(FD2(-0.50), RoundNearestTiesUp) === (FD2(0), false) - @test round_with_overflow(FD2(-0.49), RoundNearestTiesUp) === (FD2(0), false) - @test round_with_overflow(FD2(0.50), RoundNearestTiesUp) === (FD2(1), false) - @test round_with_overflow(FD2(0.51), RoundNearestTiesUp) === (FD2(1), false) - @test round_with_overflow(FD2(1.50), RoundNearestTiesUp) === (FD2(2), false) - - @test round_with_overflow(typemin(FD2), RoundNearestTiesUp) === - (parse(FD2, "-92233720368547758"), false) - @test round_with_overflow(typemax(FD2), RoundNearestTiesUp) === - (parse(FD2, "92233720368547758"), false) - - # For max, FD2 has fractional .07 so use FD3 which has .807. - @test round_with_overflow(parse(FD3, "-9223372036854775.5"), RoundNearestTiesUp) === - (FD3(-9223372036854775), false) + @test round_with_overflow(FD642(-0.51), RoundNearestTiesUp) === (FD642(-1), false) + @test round_with_overflow(FD642(-0.50), RoundNearestTiesUp) === (FD642(0), false) + @test round_with_overflow(FD642(-0.49), RoundNearestTiesUp) === (FD642(0), false) + @test round_with_overflow(FD642(0.50), RoundNearestTiesUp) === (FD642(1), false) + @test round_with_overflow(FD642(0.51), RoundNearestTiesUp) === (FD642(1), false) + @test round_with_overflow(FD642(1.50), RoundNearestTiesUp) === (FD642(2), false) + + @test round_with_overflow(typemin(FD642), RoundNearestTiesUp) === + (parse(FD642, "-92233720368547758"), false) + @test round_with_overflow(typemax(FD642), RoundNearestTiesUp) === + (parse(FD642, "92233720368547758"), false) + + # For max, FD642 has fractional .07 so use FD643 which has .807. + @test round_with_overflow(parse(FD643, "-9223372036854775.5"), RoundNearestTiesUp) === + (FD643(-9223372036854775), false) @testset "overflowing" begin - @test round_with_overflow(typemin(FD3), RoundNearestTiesUp) === - (parse(FD3, "9223372036854775.616"), true) - @test round_with_overflow(typemax(FD3), RoundNearestTiesUp) === - (parse(FD3, "-9223372036854775.616"), true) + @test round_with_overflow(typemin(FD643), RoundNearestTiesUp) === + (parse(FD643, "9223372036854775.616"), true) + @test round_with_overflow(typemax(FD643), RoundNearestTiesUp) === + (parse(FD643, "-9223372036854775.616"), true) - @test round_with_overflow(parse(FD3, "9223372036854775.5"), RoundNearestTiesUp) === - (parse(FD3, "-9223372036854775.616"), true) + @test round_with_overflow(parse(FD643, "9223372036854775.5"), RoundNearestTiesUp) === + (parse(FD643, "-9223372036854775.616"), true) end end end From cf0b5f68c4e48c609d8c7291b4573508c97aabfa Mon Sep 17 00:00:00 2001 From: Nathan Daly Date: Tue, 18 Feb 2025 16:12:44 -0700 Subject: [PATCH 19/19] Update Project.toml --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 8a3875b..29cc1ce 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "FixedPointDecimals" uuid = "fb4d412d-6eee-574d-9565-ede6634db7b0" authors = ["Fengyang Wang ", "Curtis Vogt "] -version = "0.6.2" +version = "0.6.3" [deps] BitIntegers = "c3b6d118-76ef-56ca-8cc7-ebb389d030a1"