Skip to content

Commit dfad2c9

Browse files
authored
Merge pull request #2169 from varkor/euclidean-modulo
Euclidean modulo
2 parents fd70ea3 + 0559673 commit dfad2c9

File tree

1 file changed

+112
-0
lines changed

1 file changed

+112
-0
lines changed

text/2169-euclidean-modulo.md

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
- Feature Name: `euclidean_modulo`
2+
- Start Date: 2017-10-09
3+
- RFC PR: https://github.com/rust-lang/rfcs/pull/2169
4+
- Rust Issue: https://github.com/rust-lang/rust/issues/49048
5+
6+
# Summary
7+
[summary]: #summary
8+
9+
This RFC proposes the addition of a modulo method with more useful and mathematically regular properties over the built-in remainder `%` operator when the dividend or divisor is negative, along with the associated division method.
10+
11+
For previous discussion, see: https://internals.rust-lang.org/t/mathematical-modulo-operator/5952.
12+
13+
# Motivation
14+
[motivation]: #motivation
15+
16+
The behaviour of division and modulo, as implemented by Rust's (truncated) division `/` and remainder (or truncated modulo) `%` operators, with respect to negative operands is unintuitive and has fewer useful mathematical properties than that of other varieties of division and modulo, such as flooring and Euclidean[[1]](https://dl.acm.org/citation.cfm?doid=128861.128862). While there are good reasons for this design decision[[2]](https://mail.mozilla.org/pipermail/rust-dev/2013-April/003786.html), having convenient access to a modulo operation, in addition to the remainder is very useful, and has often been requested[[3]](https://mail.mozilla.org/pipermail/rust-dev/2013-April/003680.html)[[4]](https://github.com/rust-lang/rust/issues/13909)[[5]](https://stackoverflow.com/questions/31210357/is-there-a-modulus-not-remainder-function-operation)[[6]](https://users.rust-lang.org/t/proper-modulo-support/903)[[7]](https://www.reddit.com/r/rust/comments/3yoo1q/remainder/).
17+
18+
# Guide-level explanation
19+
[guide-level-explanation]: #guide-level-explanation
20+
21+
```rust
22+
// Comparison of the behaviour of Rust's truncating division
23+
// and remainder, vs Euclidean division & modulo.
24+
(-8 / 3, -8 % 3) // (-2, -2)
25+
((-8).div_euc(3), (-8).mod_euc(3)) // (-3, 1)
26+
```
27+
Euclidean division & modulo for integers and floating-point numbers will be achieved using the `div_euc` and `mod_euc` methods. The `%` operator has identical behaviour to `mod_euc` for unsigned integers. However, when using signed integers or floating-point numbers, you should be careful to consider the behaviour you want: often Euclidean modulo will be more appropriate.
28+
29+
# Reference-level explanation
30+
[reference-level-explanation]: #reference-level-explanation
31+
32+
It is important to have both division and modulo methods, as the two operations are intrinsically linked[[8]](https://en.wikipedia.org/wiki/Modulo_operation), though it is often the modulo operator that is specifically requested.
33+
34+
A complete implementation of Euclidean modulo would involve adding 8 methods to the integer primitives in `libcore/num/mod.rs` and 2 methods to the floating-point primitives in `libcore/num` and `libstd`:
35+
```rust
36+
// Implemented for all numeric primitives.
37+
fn div_euc(self, rhs: Self) -> Self;
38+
39+
fn mod_euc(self, rhs: Self) -> Self;
40+
41+
// Implemented for all integer primitives (signed and unsigned).
42+
fn checked_div_euc(self, other: Self) -> Option<Self>;
43+
fn overflowing_div_euc(self, rhs: Self) -> (Self, bool);
44+
fn wrapping_div_euc(self, rhs: Self) -> Self;
45+
46+
fn checked_mod_euc(self, other: Self) -> Option<Self>;
47+
fn overflowing_mod_euc(self, rhs: Self) -> (Self, bool);
48+
fn wrapping_mod_euc(self, rhs: Self) -> Self;
49+
```
50+
51+
Sample implementations for `div_euc` and `mod_euc` on signed integers:
52+
```rust
53+
fn div_euc(self, rhs: Self) -> Self {
54+
let q = self / rhs;
55+
if self % rhs < 0 {
56+
return if rhs > 0 { q - 1 } else { q + 1 }
57+
}
58+
q
59+
}
60+
61+
fn mod_euc(self, rhs: Self) -> Self {
62+
let r = self % rhs;
63+
if r < 0 {
64+
return if rhs > 0 { r + rhs } else { r - rhs }
65+
}
66+
r
67+
}
68+
```
69+
And on `f64` (analagous to the `f32` implementation):
70+
```rust
71+
fn div_euc(self, rhs: f64) -> f64 {
72+
let q = (self / rhs).trunc();
73+
if self % rhs < 0.0 {
74+
return if rhs > 0.0 { q - 1.0 } else { q + 1.0 }
75+
}
76+
q
77+
}
78+
79+
fn mod_euc(self, rhs: f64) -> f64 {
80+
let r = self % rhs;
81+
if r < 0.0 {
82+
return if rhs > 0.0 { r + rhs } else { r - rhs }
83+
}
84+
r
85+
}
86+
```
87+
88+
The unsigned implementations of these methods are trivial.
89+
The `checked_*`, `overflowing_*` and `wrapping_*` methods would operate analogously to their non-Euclidean `*_div` and `*_rem` counterparts that already exist. The edge cases are identical.
90+
91+
# Drawbacks
92+
[drawbacks]: #drawbacks
93+
94+
Standard drawbacks of adding methods to primitives apply. However, with the proposed method names, there are unlikely to be conflicts downstream[[9]](https://github.com/search?q=div_euc+language%3ARust&type=Code&utf8=%E2%9C%93)[[10]](https://github.com/search?q=mod_euc+language%3ARust&type=Code&utf8=%E2%9C%93).
95+
96+
# Rationale and alternatives
97+
[alternatives]: #alternatives
98+
99+
Flooring modulo is another variant that also has more useful behaviour with negative dividends than the remainder (truncating modulo). The difference in behaviour between flooring and Euclidean division & modulo come up rarely in practice, but there are arguments in favour of the mathematical properties of Euclidean division and modulo[[1]](https://dl.acm.org/citation.cfm?doid=128861.128862). Alternatively, both methods (flooring _and_ Euclidean) could be made available, though the difference between the two is likely specialised-enough that this would be overkill.
100+
101+
The functionality could be provided as an operator. However, it is likely that the functionality of remainder and modulo are small enough that it is not worth providing a dedicated operator for the method.
102+
103+
This functionality could instead reside in a separate crate, such as `num` (floored division & modulo is already available in this crate). However, there are strong points for inclusion into core itself:
104+
- Modulo as an operation is more often desirable than remainder for signed operations (so much so that it is the default in a number of languages) -- [the mailing list discussion has more support in favour of flooring/Euclidean division](https://mail.mozilla.org/pipermail/rust-dev/2013-April/003687.html).
105+
- Many people are unaware that the remainder can cause problems with signed integers, and having a method displaying the other behaviour would draw attention to this subtlety.
106+
- The previous support for this functionality in core shows that many are keen to have this available.
107+
- The Euclidean or flooring modulo is used (or reimplemented) commonly enough that it is worth having it generally accessible, rather than in a separate crate that must be depended on by each project.
108+
109+
# Unresolved questions
110+
[unresolved]: #unresolved-questions
111+
112+
None.

0 commit comments

Comments
 (0)