Skip to content

Commit

Permalink
Add div_with_overflow (#106)
Browse files Browse the repository at this point in the history
* Remove promoting with_overflow functions.

* Bump Project.toml: 0.5.6

---------

Co-authored-by: Nathan Daly <[email protected]>
  • Loading branch information
mbarbar and NHDaly authored Jan 28, 2025
1 parent 20c21c5 commit cdd6966
Show file tree
Hide file tree
Showing 3 changed files with 43 additions and 2 deletions.
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name = "FixedPointDecimals"
uuid = "fb4d412d-6eee-574d-9565-ede6634db7b0"
authors = ["Fengyang Wang <[email protected]>", "Curtis Vogt <[email protected]>"]
version = "0.5.5"
version = "0.5.6"

[deps]
BitIntegers = "c3b6d118-76ef-56ca-8cc7-ebb389d030a1"
Expand Down
23 changes: 22 additions & 1 deletion src/FixedPointDecimals.jl
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export FixedDecimal, RoundThrows

# (Re)export checked_* arithmetic functions
# - Defined in this package:
export checked_rdiv
export checked_rdiv, div_with_overflow
# - Reexported from Base:
export checked_abs, checked_add, checked_cld, checked_div, checked_fld,
checked_mod, checked_mul, checked_neg, checked_rem, checked_sub
Expand Down Expand Up @@ -441,6 +441,27 @@ Base.Checked.mul_with_overflow(x::FD, y::FD) = Base.Checked.mul_with_overflow(pr
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)...)

# This does not exist in Base so is just part of this package.
# Throws on divide by zero.
@doc """
div_with_overflow(x::FixedDecimal{T,f}, y::FixedDecimal{T,f})::(FixedDecimal{T,f}, Bool)
where {T<:Integer, f}
Return the result of div (wrapping on overflow/underflow) and a boolean indicating whether
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
# To perform the div and overflow means reaching the max and adding 1, so typemin.
return (x, true)
end
# Note: The div() will throw for divide-by-zero, that's not an overflow.
v, b = Base.Checked.mul_with_overflow(C, div(x.i, y.i))
return (reinterpret(FD{T,f}, v), b)
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)...)
Expand Down
20 changes: 20 additions & 0 deletions test/FixedDecimal.jl
Original file line number Diff line number Diff line change
Expand Up @@ -791,6 +791,10 @@ end
@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)

@test div_with_overflow(FD{Int8,2}(1), FD{Int8,2}(0.5)) == (FD{Int8,2}(-0.56), true)
@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)

@testset "with_overflow math corner cases" begin
@testset for I in (Int128, UInt128, Int8, UInt8), f in (0,2)
T = FD{I, f}
Expand All @@ -816,6 +820,16 @@ end
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

if f > 0
@test div_with_overflow(typemax(T), eps(T))[2]
issigned(I) && @test div_with_overflow(typemin(T), eps(T))[2]
issigned(I) && @test div_with_overflow(typemax(T), -eps(T))[2]

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]
end
end
end

Expand Down Expand Up @@ -849,6 +863,12 @@ end
@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)

@test div_with_overflow(FD{Int64,6}(4), FD{Int64,6}(2)) == (FD{Int64,6}(2), false)
@test div_with_overflow(FD{Int32,6}(4), FD{Int32,6}(2.1)) == (FD{Int32,6}(1), false)
@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)
end
end

Expand Down

4 comments on commit cdd6966

@NHDaly
Copy link
Member

@NHDaly NHDaly commented on cdd6966 Jan 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JuliaRegistrator register()

Release Notes

New function:

  • div_with_overflow(a, b) -> fd, bool: Like add_with_overflow, but for truncating div.

@JuliaRegistrator
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Registration pull request created: JuliaRegistries/General/123865

Tip: Release Notes

Did you know you can add release notes too? Just add markdown formatted text underneath the comment after the text
"Release notes:" and it will be added to the registry PR, and if TagBot is installed it will also be added to the
release that TagBot creates. i.e.

@JuliaRegistrator register

Release notes:

## Breaking changes

- blah

To add them here just re-invoke and the PR will be updated.

Tagging

After the above pull request is merged, it is recommended that a tag is created on this repository for the registered package version.

This will be done automatically if the Julia TagBot GitHub Action is installed, or can be done manually through the github interface, or via:

git tag -a v0.5.6 -m "<description of version>" cdd6966ec733087e098801a4d2b1363b588ee323
git push origin v0.5.6

@NHDaly
Copy link
Member

@NHDaly NHDaly commented on cdd6966 Jan 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JuliaRegistrator register()

Release Notes

New function:

  • div_with_overflow(a, b) -> fd, bool: Like add_with_overflow, but for truncating div.

@JuliaRegistrator
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Registration pull request updated: JuliaRegistries/General/123865

Tip: Release Notes

Did you know you can add release notes too? Just add markdown formatted text underneath the comment after the text
"Release notes:" and it will be added to the registry PR, and if TagBot is installed it will also be added to the
release that TagBot creates. i.e.

@JuliaRegistrator register

Release notes:

## Breaking changes

- blah

To add them here just re-invoke and the PR will be updated.

Tagging

After the above pull request is merged, it is recommended that a tag is created on this repository for the registered package version.

This will be done automatically if the Julia TagBot GitHub Action is installed, or can be done manually through the github interface, or via:

git tag -a v0.5.6 -m "<description of version>" cdd6966ec733087e098801a4d2b1363b588ee323
git push origin v0.5.6

Please sign in to comment.