-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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 modulo #2169
Euclidean modulo #2169
Conversation
While I like the proposal, I really don't like the |
@mcarton What part of it? That it has a short suffix, or that it has a suffix and not a prefix? Both? |
How common is usage of these operations? Would it warrant adding another operator to the language? |
@Centril: I couldn't think of a good way to measure the use of these operations, as they can be implemented in various different ways. The rationale behind advocating new methods, rather than new operators, was that we could see how often they were used in Rust (say, 12 months down the line), to see whether they were used common enough to be worth considering as operators. |
@varkor this makes sense to me. However, many users will shy away from functionality until it has been stabilized, thus usage metrics for unstable stuff might not be fully representative. |
The shortness. |
If the output of these are always positive, should they return unsigned types instead? Similarly, it feels like something here should enable |
@scottmcm: I followed the precedent set by the Regarding inter-type modulo, the |
As someone who has hacked euclidean modulo as |
Alternative name possibilities?
|
Maybe |
text/0000-euclidean-modulo.md
Outdated
// Comparison of the behaviour of Rust's truncating division | ||
// and remainder, vs Euclidean division & modulo. | ||
(-8 / 3, -8 % 3) // (-2, -2) | ||
(-8.div_e(3), -8.mod_e(3)) // (-3, 1) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
.
has higher precedence than unary minus. This will be evaluated as -( 8.mod_e(3) )
i.e. -2
. You want (-8).mod_e(3)
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch, thanks!
@est31: I ran some benchmarks before making the RFC comparing the implementation for Euclidean modulo as suggested in the RFC with the branchless variant, and the RFC version turned out to be ~5x faster. It could be that the branch prediction is making the benchmarks nonrepresentative, so if you have any results to the contrary, please do share them! @tspiteri: Flooring division and modulo is subtly different (see this paper for more details — essentially Euclidean modulo always returns a nonnegative value), so avoiding the terminology |
Regarding the name: I'm happy with a change of name, provided it doesn't become cumbersome — I'd personally prefer the operation name to come first for aiding discoverability (for example, if other methods of rounding are added in the future) via autocomplete, etc. Perhaps |
@varkor I haven't run any benchmarks and was just guessing that a branchless version is faster. The code path is definitely not hot so I didn't care much. |
@varkor Yes, flooring and Euclidean division are different; to use |
@est31 Your version can overflow. |
@tspiteri I agree that it makes sense to consider what other languages do but it shouldn't be the only argument -- Rust does a lot of things better/differently than other languages. I don't see how differences to other software would be a disadvantage for Rust's modulo function, given that the modulo operator You can also view the bandwagon argument from the opposite side: In mathematics, Euclidean modulo is standard that everyone uses, presumably because it has the most regular properties. I also don't think the arguments in the paper are a matter of taste: the code examples really are more uniform if Euclidean division is used. That is anecdotal evidence but I don't think we have any better kind of evidence. That said, I don't care very much whether it's flooring or Euclidean modulo. I just think the discussion should include more than personal preference and the bandwagon argument. (Also, I'm kind of afraid this proposal will die because of bikeshedding about this.) |
Can you elaborate on usage of negative-divisor modulo in mathematics? I'm only familiar with strictly-positive ones, and for that flooring and euclidean are the same. |
Rambling a bit here: Parroting mathematical usage can just as well be considered 'the bandwagon argument': π is the standard that everyone uses, mostly for the sake of tradition, even though τ has better mathematical properties. Euclidean division is defined the way it is because it aligns well with Euclid's division theorem for integers (which requires the remainder to satisfy 0 ≤ r < |d|). This theorem in turn is formulated this way presumably because this is the most concise way to make the division result unique. But this property cannot be maintained in general Euclidean domains (the 'proper' structure to study division-with-remainder), where a total order relation may fail to exist, nor there may be any way to make the result 'naturally' unique (Gaussian integers, polynomials). From this more general perspective, floor division is just as good a definition of division-with-remainder as the 'Euclidean' division. I see no properly mathematical reason to prefer one over the other. And I don't recall any programming problem where a division-with-remainder by a negative divisor is meaningful, so from this point of view, there also seems to be no reason to prefer either. (Well, I think I can imagine a problem where the choice of definition may conceivably matter, but I still don't see how either is more advantageous). That perspective, however, would suggest also adding a ceiling division operator (it's the answer to the question 'how many buckets of capacity n do you need to contain N items?') and a combined division-modulo operation (which answers the problem 'given a scalar offset into a two-dimensional array, what are its corresponding two-dimensional cordinates?'). Admittedly the former can be expressed with |
+1 for a combined division-modulo operation. |
Rename `div_e` and `mod_e` to `div_euc` and `mod_euc`, respectively. This is more descriptive whilst still being relatively concise.
I've updated the method names in the proposal with a more descriptive Additionally, I've extended the RFC slightly to include Euclidean division and modulo methods for Regarding the discussion about flooring versus Euclidean division/modulo that arose: I think @fstirlitz summed it up well; in practice, there will very rarely be a difference between the results of flooring and Euclidean modulo/division — taking the negative modulo of a number is a very uncommon operation: it's just a matter of deciding what the behaviour in this particular edge case should be. My feeling was, and the general feeling on the internals thread seemed to be, that Euclidean modulo was the least surprising of the two (and mathematical consistency is a bonus). I'd be hesitant to add a combined division-modulo method in this RFC — it seems like a separate, though tangentially related, issue — and it'd be better not to crowd this RFC with new methods. |
Following on from @est31's point: I don't see the advantage to spelling the name I'd like to know what other people think about changing the return type before making any changes, though. It'd be really nice to finalise this RFC at last. |
About the return type, it depends on whether this is meant to look like the other arithmetic operators, or like other inherent functions. For operators on primitives I would expect the output to be like |
And another thing, returning unsigned values suggests to me that this is only intended for indexing, not as a normal division operation where operating on signed integers should return a signed integer. But in that case it would make more sense to call it something like |
@tspiteri makes a good point. I think that considering It's unfortunate that the signature will mean casting is required in some cases, but the consistency seems more important here. |
@sfackler: is it possible to follow up regarding the naming convention — taking into account the points others have made, is there strong motivation for requiring After that has been resolved, are there any other issues stalling this RFC from a FCP? |
Team member @sfackler has proposed to merge this. The next step is review by the rest of the tagged teams: No concerns currently listed. Once a majority of reviewers approve (and none object), this will enter its final comment period. If you spot a major issue that hasn't been raised at any point in this process, please speak up! See this document for info about what commands tagged team members can give me. |
🔔 This is now entering its final comment period, as per the review above. 🔔 |
But it's also adding new methods, so maybe that just means the set of added methods isn't sufficient yet. Conveniently, there's even naming and semantic precedent for it in You can have (Their types being |
This signature (and similarly if On the other hand, the current signature is very amenable to modification in the future when implicit widening is introduced, which should eliminate the problems entirely. (Additionally, for consistency purposes, such a signature for |
Regarding |
Hold up, why is rfcbot only listing four members of the libs team? The libs team has eight people on it. |
We subdivided the libs team a bit. |
The final comment period is now complete. |
Alright! FCP has now elapsed and it looks like there were no major things brought up, so I'm going to merge! |
Updated Rendered link + added tracking issue to top post. |
Proposal to add Euclidean modulo & division functionality for integers and floating-point numbers, to address common issues with taking remainders involving negative numbers.
Internals discussion here.
Rendered.
Tracking issue.