Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

div with rounding modes [+ rounded division] #33040

Merged
merged 8 commits into from
Sep 28, 2019
Merged

div with rounding modes [+ rounded division] #33040

merged 8 commits into from
Sep 28, 2019

Conversation

Keno
Copy link
Member

@Keno Keno commented Aug 23, 2019

I found myself needing a rounding integer division, i.e. one that does RoundToNearestTiesAway (actually the reference implementation calls for RoundToNearestTiesToward, but the paper doesn't specified and either should be ok). Rather than adding a rntad (round-to-neearest-ties-away-division) function, I figured it may make sense to instead create a generic div(x, y, r::RoundingMode=RoundToZero) function, while keeping fld,cld (and the two-argument div) as useful shorthands for the rounding modes they represent.

This PR just does the re-arranging for the existing code. I haven't yet added the new functionality for round-nearest. Let's discuss whether this direction is sensible.

@ararslan ararslan added the maths Mathematical functions label Aug 23, 2019
@Keno
Copy link
Member Author

Keno commented Aug 23, 2019

Somehow I missed that we already have rem(x, y, r::RoundingMode) with compatible semantics, which I thinks validates the sensibility here.

@StefanKarpinski
Copy link
Member

I feel like @simonbyrne would have the most well-informed opinion on this matter but it seems like a solid idea to me?

@simonbyrne
Copy link
Contributor

👍 this was my plan from #9283.

@Keno Keno changed the title RFC: div with rounding modes div with rounding modes [+ rounded division] Aug 23, 2019
@Keno Keno requested a review from simonbyrne August 23, 2019 04:07
base/div.jl Outdated
"""
div(x, y, r::RoundingMode=RoundToZero)

Compute the remainder of `x` after integer division by `y`, with the quotient rounded
Copy link
Contributor

Choose a reason for hiding this comment

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

unindent this text

base/div.jl Outdated
return q + copysign(1, q)
else
@assert rnd == RoundNearestTiesUp
return sign(q) == -1 ? q : q + 1
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think it handles negative odd values of y correctly, e.g. divrem(5, -3, RoundNearestTiesUp).

base/div.jl Outdated
typeof(RoundNearestTiesAway),
typeof(RoundNearestTiesUp)})
(q, r) = divrem(x, y)
halfy = copysign(y >> 1, r)
Copy link
Contributor

Choose a reason for hiding this comment

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

would be good to explain the logic of this function: I don't quite understand it myself.

test/int.jl Outdated
(3, 2, 2, 2, 2),
(-3, 2, -2, -2, -1),
(5, 2, 2, 3, 3),
(-5, 2, -2, -3, -2))
Copy link
Contributor

Choose a reason for hiding this comment

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

Add some odd divisor cases to the test cases. Also typemin/typemax divisors/dividends.

base/div.jl Outdated
c == -1 && return q
c == 1 && return q + copysign(1, q)
if rnd == RoundNearest
return iseven(q) ? q : q + copysign(1, q)
Copy link
Contributor

Choose a reason for hiding this comment

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

None of these handle y == 1 correctly.

@Keno Keno force-pushed the kf/3div branch 3 times, most recently from c5aa5ee to 1a374a7 Compare August 24, 2019 22:10
base/div.jl Outdated
# The divisior is odd - no ties are possible, just
# round to nearest. N.B. 2r == y is impossible because
# y is odd.
return abs(2r) > abs(y) ? q + copysign(one(q), q) : q
Copy link
Contributor

Choose a reason for hiding this comment

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

This won't work if 2r overflows, e.g. x,y = typemax(Int)-2,typemax(Int)

@simonbyrne
Copy link
Contributor

simonbyrne commented Aug 25, 2019

@Keno I've added some code that I think should handle overflow correctly. Tests could be improved, and I'm not really sure they will handle mixed Int/UInt arguments correctly. Feel free to adapt as you see fit.

@Keno
Copy link
Member Author

Keno commented Aug 25, 2019

Oh, I was just gonna use checked_mul, but I like that your version has divrem. We could switch all the divisions to bitshifts, or maybe we just trust that the compiler will figure it out (and that gmp has sufficient special cases). We should also definitely add your overflow test case.

base/div.jl Outdated
div(a, b) = div(a, b, RoundToZero)

"""
rem(x, y, r::RoundingMode)
Copy link

Choose a reason for hiding this comment

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

maybe rem(x, y, r::RoundingMode=RoundToZero) as div docstring

@simonbyrne
Copy link
Contributor

ooh, using checked_mul is a good idea. It should be possible to rewrite my code to use that instead.

@Keno
Copy link
Member Author

Keno commented Aug 26, 2019

(Just as a note, I meant mul_with_overflow, the checked_ one throws).

Keno added a commit to JuliaCrypto/ToyFHE.jl that referenced this pull request Sep 25, 2019
Keno and others added 5 commits September 25, 2019 17:04
In preparation for supporting other rounding modes in div, create
a three-argument div function that takes a rounding mode as the last
argument and make this the fundamental fallback for fld/cld.
@Keno
Copy link
Member Author

Keno commented Sep 25, 2019

Rebased and fixed a few more issues. If CI passes, I'd like to start getting this in.

@simonbyrne
Copy link
Contributor

If we want to check for overflow, we could exhaustively test Int8.

@Keno
Copy link
Member Author

Keno commented Sep 27, 2019

If we want to check for overflow, we could exhaustively test Int8.

Good idead. Done. This should be good to go from my side.

I think it's better to give a MethodError here than an approximate
answer for non-AbstractFloat reals (e.g. custom integer types).
@Keno Keno merged commit a3eb9d4 into master Sep 28, 2019
@Keno Keno deleted the kf/3div branch September 28, 2019 19:17
rfourquet pushed a commit to rfourquet/BitIntegers.jl that referenced this pull request Sep 30, 2019
See JuliaLang/julia#33040 (a3eb9d4).
This commit enables the new three-argument div API for BitIntegers.
NHDaly added a commit to NHDaly/julia that referenced this pull request Oct 28, 2019
It looks like in JuliaLang#33040 the `div` docstring was accidentally incorrectly copied from the `rem` docstring, so it currently describes the `rem` operation, not `div`.

This commit changes that docstring to correctly describe integer division with custom rounding.
Keno pushed a commit that referenced this pull request Oct 29, 2019
It looks like in #33040 the `div` docstring was accidentally incorrectly copied from the `rem` docstring, so it currently describes the `rem` operation, not `div`.

This commit changes that docstring to correctly describe integer division with custom rounding.
@NHDaly
Copy link
Member

NHDaly commented Jan 26, 2020

I noticed that fld1 isn't included in this change, and it looks like it's still calling two-argument div:

fld1(x::T, y::T) where {T<:Real} = (m = mod1(x, y); fld(x + y - m, y))

Should packages still implement a custom override for fld1 if needed? (I'm looking at adding this support for FixedDecimals, and i'm wondering if this can be as simple as just adding Base.div(x::T, y::T, r::RoundingMode) where {T <: FD} = T(div(x.i, y.i, r))? Do I need to stop defining two-arg-div, fld, fld1, etc?)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
maths Mathematical functions
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants