From e84b3b9935b096be38bdb4eaa8f55aebad8428b1 Mon Sep 17 00:00:00 2001 From: varkor Date: Mon, 9 Oct 2017 20:00:08 +0100 Subject: [PATCH 1/8] Add an initial proposal for Euclidean modulo --- text/0000-euclidean-modulo.md | 90 +++++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 text/0000-euclidean-modulo.md diff --git a/text/0000-euclidean-modulo.md b/text/0000-euclidean-modulo.md new file mode 100644 index 00000000000..03dccab8e88 --- /dev/null +++ b/text/0000-euclidean-modulo.md @@ -0,0 +1,90 @@ +- Feature Name: euclidean_modulo +- Start Date: 2017-10-09 +- RFC PR: +- Rust Issue: + +# Summary +[summary]: #summary + +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. + +# Motivation +[motivation]: #motivation + +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/). + +# Guide-level explanation +[guide-level-explanation]: #guide-level-explanation + +```rust +// 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) +``` +Euclidean division & modulo will be achieved using the `div_e` and `mod_e` methods. The `%` operator has identical behaviour to `mod_e` for unsigned integers. However, when using signed integers, you should be careful to consider the behaviour you want: often Euclidean modulo will be more appropriate. + +# Reference-level explanation +[reference-level-explanation]: #reference-level-explanation + +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. + +A complete implementation of Euclidean modulo would involve adding 8 methods to the integer primitives in `libcore/num/mod.rs` (floating-point implementations would also be provided for `div_e` and `mod_e`): +```rust +// Implemented for all numeric primitives. +fn div_e(self, rhs: Self) -> Self; + +fn mod_e(self, rhs: Self) -> Self; + +// Implemented for all integer primitives (signed and unsigned). +fn checked_div_e(self, other: Self) -> Option; +fn overflowing_div_e(self, rhs: Self) -> (Self, bool); +fn wrapping_div_e(self, rhs: Self) -> Self; + +fn checked_mod_e(self, other: Self) -> Option; +fn overflowing_mod_e(self, rhs: Self) -> (Self, bool); +fn wrapping_mod_e(self, rhs: Self) -> Self; +``` + +Sample implementations for `div_e` and `mod_e` on signed integers: +```rust +fn div_e(self, rhs: Self) -> Self { + let q = self / rhs; + if self % rhs < 0 { + return if rhs > 0 { q - 1 } else { q + 1 } + } + q +} + +fn mod_e(self, rhs: Self) -> Self { + let r = self % rhs; + if r < 0 { + return if rhs > 0 { r + rhs } else { r - rhs } + } + r +} +``` + +The unsigned implementations of these methods are trivial. +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. + +# Drawbacks +[drawbacks]: #drawbacks + +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_e+language%3ARust&type=Code&utf8=%E2%9C%93)[[10]](https://github.com/search?q=mod_e+language%3ARust&type=Code&utf8=%E2%9C%93). + +# Rationale and alternatives +[alternatives]: #alternatives + +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. + +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. + +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: 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); 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; the previous support for this functionality in core shows that many are keen to have this available; 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. + +Alternatives have been discussed at [https://internals.rust-lang.org/t/mathematical-modulo-operator/5952](). + +# Unresolved questions +[unresolved]: #unresolved-questions + +None. \ No newline at end of file From b287ad9399059ee5f7c6a592b37b792cf19e311b Mon Sep 17 00:00:00 2001 From: varkor Date: Mon, 9 Oct 2017 22:39:56 +0100 Subject: [PATCH 2/8] Improve the readability of the proposal --- text/0000-euclidean-modulo.md | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/text/0000-euclidean-modulo.md b/text/0000-euclidean-modulo.md index 03dccab8e88..9967fadd523 100644 --- a/text/0000-euclidean-modulo.md +++ b/text/0000-euclidean-modulo.md @@ -8,6 +8,8 @@ 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. +For previous discussion, see: [https://internals.rust-lang.org/t/mathematical-modulo-operator/5952](). + # Motivation [motivation]: #motivation @@ -22,14 +24,14 @@ The behaviour of division and modulo, as implemented by Rust's (truncated) divis (-8 / 3, -8 % 3) // (-2, -2) (-8.div_e(3), -8.mod_e(3)) // (-3, 1) ``` -Euclidean division & modulo will be achieved using the `div_e` and `mod_e` methods. The `%` operator has identical behaviour to `mod_e` for unsigned integers. However, when using signed integers, you should be careful to consider the behaviour you want: often Euclidean modulo will be more appropriate. +Euclidean division & modulo for integers will be achieved using the `div_e` and `mod_e` methods. The `%` operator has identical behaviour to `mod_e` for unsigned integers. However, when using signed integers, you should be careful to consider the behaviour you want: often Euclidean modulo will be more appropriate. # Reference-level explanation [reference-level-explanation]: #reference-level-explanation 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. -A complete implementation of Euclidean modulo would involve adding 8 methods to the integer primitives in `libcore/num/mod.rs` (floating-point implementations would also be provided for `div_e` and `mod_e`): +A complete implementation of Euclidean modulo would involve adding 8 methods to the integer primitives in `libcore/num/mod.rs`: ```rust // Implemented for all numeric primitives. fn div_e(self, rhs: Self) -> Self; @@ -80,11 +82,13 @@ Flooring modulo is another variant that also has more useful behaviour with nega 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. -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: 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); 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; the previous support for this functionality in core shows that many are keen to have this available; 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. - -Alternatives have been discussed at [https://internals.rust-lang.org/t/mathematical-modulo-operator/5952](). +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: +- 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). +- 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. +- The previous support for this functionality in core shows that many are keen to have this available. +- 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. # Unresolved questions [unresolved]: #unresolved-questions -None. \ No newline at end of file +None. From 1b3bf49019d7c9db046dd211d46d41232a8db3a0 Mon Sep 17 00:00:00 2001 From: varkor Date: Mon, 9 Oct 2017 23:21:06 +0100 Subject: [PATCH 3/8] Consider floating-point implementations --- text/0000-euclidean-modulo.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0000-euclidean-modulo.md b/text/0000-euclidean-modulo.md index 9967fadd523..cae87c56206 100644 --- a/text/0000-euclidean-modulo.md +++ b/text/0000-euclidean-modulo.md @@ -91,4 +91,4 @@ This functionality could instead reside in a separate crate, such as `num` (floo # Unresolved questions [unresolved]: #unresolved-questions -None. +- Is it worth implementing `div_e` and `mod_e` for floating-point numbers? While it makes sense for completeness, it may be too rare a use case to be worth extending the core library to include. From ffb409d3442fc92f84359bbc667f7e400ca17c8b Mon Sep 17 00:00:00 2001 From: varkor Date: Mon, 9 Oct 2017 23:24:22 +0100 Subject: [PATCH 4/8] Fix link --- text/0000-euclidean-modulo.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0000-euclidean-modulo.md b/text/0000-euclidean-modulo.md index cae87c56206..0977cf3a31e 100644 --- a/text/0000-euclidean-modulo.md +++ b/text/0000-euclidean-modulo.md @@ -8,7 +8,7 @@ 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. -For previous discussion, see: [https://internals.rust-lang.org/t/mathematical-modulo-operator/5952](). +For previous discussion, see: https://internals.rust-lang.org/t/mathematical-modulo-operator/5952. # Motivation [motivation]: #motivation From 7e3bc84c9e2511a8876214c1b80c64eef7029505 Mon Sep 17 00:00:00 2001 From: varkor Date: Wed, 18 Oct 2017 19:00:04 +0100 Subject: [PATCH 5/8] Fixed an operator precedence issue --- text/0000-euclidean-modulo.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/text/0000-euclidean-modulo.md b/text/0000-euclidean-modulo.md index 0977cf3a31e..05c1088045d 100644 --- a/text/0000-euclidean-modulo.md +++ b/text/0000-euclidean-modulo.md @@ -21,8 +21,8 @@ The behaviour of division and modulo, as implemented by Rust's (truncated) divis ```rust // 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) +(-8 / 3, -8 % 3) // (-2, -2) +((-8).div_e(3), (-8).mod_e(3)) // (-3, 1) ``` Euclidean division & modulo for integers will be achieved using the `div_e` and `mod_e` methods. The `%` operator has identical behaviour to `mod_e` for unsigned integers. However, when using signed integers, you should be careful to consider the behaviour you want: often Euclidean modulo will be more appropriate. From cb8bf0aac3d6be3140fe109f30b9a12d317e26cb Mon Sep 17 00:00:00 2001 From: varkor Date: Mon, 23 Oct 2017 21:10:09 +0100 Subject: [PATCH 6/8] Change the name of the Euclidean division/modulo operators Rename `div_e` and `mod_e` to `div_euc` and `mod_euc`, respectively. This is more descriptive whilst still being relatively concise. --- text/0000-euclidean-modulo.md | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/text/0000-euclidean-modulo.md b/text/0000-euclidean-modulo.md index 05c1088045d..01ef6c46b5c 100644 --- a/text/0000-euclidean-modulo.md +++ b/text/0000-euclidean-modulo.md @@ -22,9 +22,9 @@ The behaviour of division and modulo, as implemented by Rust's (truncated) divis // 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) +((-8).div_euc(3), (-8).mod_euc(3)) // (-3, 1) ``` -Euclidean division & modulo for integers will be achieved using the `div_e` and `mod_e` methods. The `%` operator has identical behaviour to `mod_e` for unsigned integers. However, when using signed integers, you should be careful to consider the behaviour you want: often Euclidean modulo will be more appropriate. +Euclidean division & modulo for integers 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, you should be careful to consider the behaviour you want: often Euclidean modulo will be more appropriate. # Reference-level explanation [reference-level-explanation]: #reference-level-explanation @@ -34,23 +34,23 @@ It is important to have both division and modulo methods, as the two operations A complete implementation of Euclidean modulo would involve adding 8 methods to the integer primitives in `libcore/num/mod.rs`: ```rust // Implemented for all numeric primitives. -fn div_e(self, rhs: Self) -> Self; +fn div_euc(self, rhs: Self) -> Self; -fn mod_e(self, rhs: Self) -> Self; +fn mod_euc(self, rhs: Self) -> Self; // Implemented for all integer primitives (signed and unsigned). -fn checked_div_e(self, other: Self) -> Option; -fn overflowing_div_e(self, rhs: Self) -> (Self, bool); -fn wrapping_div_e(self, rhs: Self) -> Self; +fn checked_div_euc(self, other: Self) -> Option; +fn overflowing_div_euc(self, rhs: Self) -> (Self, bool); +fn wrapping_div_euc(self, rhs: Self) -> Self; -fn checked_mod_e(self, other: Self) -> Option; -fn overflowing_mod_e(self, rhs: Self) -> (Self, bool); -fn wrapping_mod_e(self, rhs: Self) -> Self; +fn checked_mod_euc(self, other: Self) -> Option; +fn overflowing_mod_euc(self, rhs: Self) -> (Self, bool); +fn wrapping_mod_euc(self, rhs: Self) -> Self; ``` -Sample implementations for `div_e` and `mod_e` on signed integers: +Sample implementations for `div_euc` and `mod_euc` on signed integers: ```rust -fn div_e(self, rhs: Self) -> Self { +fn div_euc(self, rhs: Self) -> Self { let q = self / rhs; if self % rhs < 0 { return if rhs > 0 { q - 1 } else { q + 1 } @@ -58,7 +58,7 @@ fn div_e(self, rhs: Self) -> Self { q } -fn mod_e(self, rhs: Self) -> Self { +fn mod_euc(self, rhs: Self) -> Self { let r = self % rhs; if r < 0 { return if rhs > 0 { r + rhs } else { r - rhs } @@ -73,7 +73,7 @@ The `checked_*`, `overflowing_*` and `wrapping_*` methods would operate analogou # Drawbacks [drawbacks]: #drawbacks -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_e+language%3ARust&type=Code&utf8=%E2%9C%93)[[10]](https://github.com/search?q=mod_e+language%3ARust&type=Code&utf8=%E2%9C%93). +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). # Rationale and alternatives [alternatives]: #alternatives @@ -91,4 +91,4 @@ This functionality could instead reside in a separate crate, such as `num` (floo # Unresolved questions [unresolved]: #unresolved-questions -- Is it worth implementing `div_e` and `mod_e` for floating-point numbers? While it makes sense for completeness, it may be too rare a use case to be worth extending the core library to include. +- Is it worth implementing `div_euc` and `mod_euc` for floating-point numbers? While it makes sense for completeness, it may be too rare a use case to be worth extending the core library to include. From 1650689ab9872dcad5efe43de9fabe3c7d3389b0 Mon Sep 17 00:00:00 2001 From: varkor Date: Mon, 23 Oct 2017 21:30:29 +0100 Subject: [PATCH 7/8] Add floating-point Euclidean division and modulo --- text/0000-euclidean-modulo.md | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/text/0000-euclidean-modulo.md b/text/0000-euclidean-modulo.md index 01ef6c46b5c..8eeb4e91317 100644 --- a/text/0000-euclidean-modulo.md +++ b/text/0000-euclidean-modulo.md @@ -24,14 +24,14 @@ The behaviour of division and modulo, as implemented by Rust's (truncated) divis (-8 / 3, -8 % 3) // (-2, -2) ((-8).div_euc(3), (-8).mod_euc(3)) // (-3, 1) ``` -Euclidean division & modulo for integers 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, you should be careful to consider the behaviour you want: often Euclidean modulo will be more appropriate. +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. # Reference-level explanation [reference-level-explanation]: #reference-level-explanation 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. -A complete implementation of Euclidean modulo would involve adding 8 methods to the integer primitives in `libcore/num/mod.rs`: +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`: ```rust // Implemented for all numeric primitives. fn div_euc(self, rhs: Self) -> Self; @@ -66,6 +66,24 @@ fn mod_euc(self, rhs: Self) -> Self { r } ``` +And on `f64` (analagous to the `f32` implementation): +```rust +fn div_euc(self, rhs: f64) -> f64 { + let q = (self / rhs).trunc(); + if self % rhs < 0.0 { + return if rhs > 0.0 { q - 1.0 } else { q + 1.0 } + } + q +} + +fn mod_euc(self, rhs: f64) -> f64 { + let r = self % rhs; + if r < 0.0 { + return if rhs > 0.0 { r + rhs } else { r - rhs } + } + r +} +``` The unsigned implementations of these methods are trivial. 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. @@ -91,4 +109,4 @@ This functionality could instead reside in a separate crate, such as `num` (floo # Unresolved questions [unresolved]: #unresolved-questions -- Is it worth implementing `div_euc` and `mod_euc` for floating-point numbers? While it makes sense for completeness, it may be too rare a use case to be worth extending the core library to include. +None. From 0559673aeef8eb2bdf39b133cd0ff1a4a112acd5 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 15 Mar 2018 08:26:04 -0700 Subject: [PATCH 8/8] RFC 2169: Euclidean Modulo --- text/{0000-euclidean-modulo.md => 2169-euclidean-modulo.md} | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) rename text/{0000-euclidean-modulo.md => 2169-euclidean-modulo.md} (97%) diff --git a/text/0000-euclidean-modulo.md b/text/2169-euclidean-modulo.md similarity index 97% rename from text/0000-euclidean-modulo.md rename to text/2169-euclidean-modulo.md index 8eeb4e91317..4d9904adb76 100644 --- a/text/0000-euclidean-modulo.md +++ b/text/2169-euclidean-modulo.md @@ -1,7 +1,7 @@ -- Feature Name: euclidean_modulo +- Feature Name: `euclidean_modulo` - Start Date: 2017-10-09 -- RFC PR: -- Rust Issue: +- RFC PR: https://github.com/rust-lang/rfcs/pull/2169 +- Rust Issue: https://github.com/rust-lang/rust/issues/49048 # Summary [summary]: #summary