-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
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
Euclidean division: div
, rem
and friends
#9283
Comments
How would people feel about having |
Always in Int? What if my arguments were BigInt? Or I used a new decimal floating point type? (the result would be an integer value, but you don't want to lose that it is a decimal floating point type). |
@simonbyrne I think you are confusing the concrete type |
For |
I should have expanded more. What I was thinking is that
I guess this was the crux of my question: are there any use cases where it would be more useful to return a value of the same type as the arguments, as opposed to a subtype of @tkelman I was actually thinking |
Though we might have to throw errors in the case of |
@simonbyrne I know I would not like div to return a different type than what I gave it... I think it might mess up later calculations, and how would you know what the mapping should be? Float32 goes to Int32, Float64 -> Int64, Float128 -> Int128, maybe, but it still seems a bit of a mess that you have to know in div what Integer type can hold any possible integral value of an arbitrary type. |
No, the return types would just be The opposite question is then: if we return a floating point value of the same type, what should we return when the answer is greater than julia> div(Int(8*maxintfloat(Float64)),5) # exact value
14411518807585587 Now, 14411518807585587 is not exactly representable by a |
@simonbyrne What is the problem there? you are doing a div on an Int, not on a Float64... |
julia> Int64(div(8*maxintfloat(Float64), 5))
14411518807585588 Off by one. |
I still don't see how that has anything to do with div... i.e.
and this is what would happen on a 32-bit machine:
|
Not relevant to the question, but thanks for pointing out the
Anyway, FWIW, I agree that |
It is only representable because you picked a number where it increased the exponent... but you are definitely past the range of representable integers. |
Well, Simon picked it, but yes, it was picked purposely. It's also perfectly representable (but the 15 numbers before or after are not). And no Julia doesn't promote automatically, because that would preclude various useful optimizations. Cheers! |
@kmsquire I wasn’t trying to say that Julia should promote automatically, but rather, that that is what provokes this “problem”. I still don’t see it as really being a problem though, I think if you care about exact results, you need to think about promoting things yourself, since Julia won’t do it for you. |
When I wrote a decimal floating point package ages ago, I simply “promoted” things internally to 128 bits before performing the operations (which were only 6, +, -, *, /, \ (div), and # (rem)). [the numbers were represented as 64-bit signed #s with a signed byte exponent]. The nice thing was that even on 16-bit platforms, the PDP-11, DG, and MS-DOS, you’d get exactly the same answer as on 32-bit machines, all out to 19 digits... (and this was back when there was no standard for binary floating point... you’d get different answers on every platform!). Could you not promote things just internally to the div() operation? |
What do you mean? I'll leave One other slightly crazy idea is to define a |
How would a three-bit unsigned integer type help? |
Technically you only need 2 bits: if you do I'm not sure why |
I've updated the proposal (I've intentionally left off computing the trailing bits of the quotient, as that discussion seemed to have gotten sidetracked). |
I'm going to purge a bunch of irrelevant conversation from this thread, which can be seen here for posterity: https://gist.github.com/StefanKarpinski/353b2f22b18b0bb3a74638784f72ebc7. |
Small remark: the operation described above is not Euclidean division, but rather truncated division. The Euclidean remainder of rational numbers, floating point numbers is always 0, as division is always possible for nonzero divisors. See https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/divmodnote-letter.pdf For the Euclidean division of integers, the remainder should always be nonnegative -- I've just been bitten by that when implementing computation of Hermite forms (but in another language). |
@denisrosset thanks for the link, it's an interesting read. I used the term "Euclidean" to refer to any division that computes an integer quotient. I wanted to avoid "integer division", as that might imply "division of integers". Unfortunately it seems that computer scientists just seem to call it "division" ignoring completely ignoring numbers other than integers. Avoiding confusing/ambiguous terminology was exactly the purpose of this issue. Note that the Euclidean remainder can be non-zero for floating point numbers, since the quotient is required to be an integer, i.e.
|
@simonbyrne : on an Euclidean domain, both
What we have here is truncated division, which needs only an additive group and a total order, and indeed works on floating point numbers, rational numbers, algebraic numbers, etc... as you mention. |
Closed by #33040. |
Closing a four-digit issue ain't bad these days 🎉 |
For any two real numbers
x
andy
, Euclidean division is the problem of finding an integerq
(the quotient) and numberr
(the remainder, on an interval of lengthabs(y)
) such thatWe currently offer 3 methods for computing
q
andr
div(x,y)
trunc(x/y)
rem(x,y)
fld(x,y)
floor(x/y)
mod(x,y)
cld(x,y)
ceil(x/y)
mod(x,-y)
Unfortunately, none of these actually correspond to the
remainder
function in the IEEE-754 standard, which is defined as the remainder afterx/y
has been rounded to the nearest integer, ties to even (this is also theremainder
function in C).My proposal is then to implement these by dispatching
div
,rem
anddivrem
onRoundingMode
. This is similar to what we do forround
. e.g.:as well as appropriate definitions for
RoundNearest
,RoundNearestTiesAway
andRoundNearestTiesUp
. These would all have the properties that:div(x, y, r)
is equal toround(x/y, r)
(if computed without intermediate rounding)x == div(x, y, r)*y + rem(x, y, r)
(if computed without intermediate rounding)The first steps toward this were made in #10946 (defining
rem(x,y, ::RoundingMode)
for floating point arguments, but the remainder (no pun intended) is still to be done.Another thing that would be useful to have, is something like C'sremquo
function, which returns the congruent modulo of the quotient, which is handy for massive range reductions (e.g. forsind
).The text was updated successfully, but these errors were encountered: