fix incorrect overflow handling for rem_euclid_int
This commit is contained in:
parent
e8f5a8238e
commit
e0f3eb77b8
10
README.md
10
README.md
|
@ -74,6 +74,11 @@ The conversions supported cover the following cases.
|
|||
|
||||
## What’s new
|
||||
|
||||
### Version 0.5.4 news (unreleased)
|
||||
|
||||
* Bug fix: [`rem_euclid_int`] and its checked versions were handling
|
||||
overflow incorrectly.
|
||||
|
||||
### Version 0.5.3 news (2020-02-13)
|
||||
|
||||
* Bug fix: [`round_to_zero`] was returning incorrect results for
|
||||
|
@ -95,6 +100,8 @@ The conversions supported cover the following cases.
|
|||
* The following methods were added to the [`Wrapping`] wrapper:
|
||||
* [`div_euclid`][wde], [`rem_euclid`][wre]
|
||||
* [`div_euclid_int`][wdei], [`rem_euclid_int`][wrei]
|
||||
* The following methods were deprecated:
|
||||
* [`wrapping_rem_int`], [`overflowing_rem_int`]
|
||||
|
||||
### Version 0.5.2 news (2020-02-02)
|
||||
|
||||
|
@ -109,10 +116,13 @@ The conversions supported cover the following cases.
|
|||
[`checked_rem`]: https://docs.rs/fixed/0.5.3/fixed/struct.FixedI32.html#method.checked_rem
|
||||
[`div_euclid`]: https://docs.rs/fixed/0.5.3/fixed/struct.FixedI32.html#method.div_euclid
|
||||
[`overflowing_div_euclid`]: https://docs.rs/fixed/0.5.3/fixed/struct.FixedI32.html#method.overflowing_div_euclid
|
||||
[`overflowing_rem_int`]: https://docs.rs/fixed/0.5.3/fixed/struct.FixedI32.html#method.overflowing_rem_int
|
||||
[`rem_euclid_int`]: https://docs.rs/fixed/0.5.3/fixed/struct.FixedI32.html#method.rem_euclid_int
|
||||
[`rem_euclid`]: https://docs.rs/fixed/0.5.3/fixed/struct.FixedI32.html#method.rem_euclid
|
||||
[`round_to_zero`]: https://docs.rs/fixed/0.5.3/fixed/struct.FixedI32.html#method.round_to_zero
|
||||
[`saturating_div_euclid`]: https://docs.rs/fixed/0.5.3/fixed/struct.FixedI32.html#method.saturating_div_euclid
|
||||
[`wrapping_div_euclid`]: https://docs.rs/fixed/0.5.3/fixed/struct.FixedI32.html#method.wrapping_div_euclid
|
||||
[`wrapping_rem_int`]: https://docs.rs/fixed/0.5.3/fixed/struct.FixedI32.html#method.wrapping_rem_int
|
||||
[issue 13]: https://gitlab.com/tspiteri/fixed/issues/13
|
||||
[wde]: https://docs.rs/fixed/0.5.3/fixed/struct.Wrapping.html#method.div_euclid
|
||||
[wdei]: https://docs.rs/fixed/0.5.3/fixed/struct.Wrapping.html#method.div_euclid_int
|
||||
|
|
|
@ -5,6 +5,12 @@ modification, are permitted in any medium without royalty provided the
|
|||
copyright notice and this notice are preserved. This file is offered
|
||||
as-is, without any warranty. -->
|
||||
|
||||
Version 0.5.4 (unreleased)
|
||||
==========================
|
||||
|
||||
* Bug fix: `rem_euclid_int` and its checked versions were handling
|
||||
overflow incorrectly.
|
||||
|
||||
Version 0.5.3 (2020-02-13)
|
||||
==========================
|
||||
|
||||
|
@ -27,6 +33,8 @@ Version 0.5.3 (2020-02-13)
|
|||
* The following methods were added to the `Wrapping` wrapper:
|
||||
* `div_euclid`, `rem_euclid`
|
||||
* `div_euclid_int`, `rem_euclid_int`
|
||||
* The following methods were deprecated:
|
||||
* `wrapping_rem_int`, `overflowing_rem_int`
|
||||
|
||||
Version 0.5.2 (2020-02-02)
|
||||
==========================
|
||||
|
|
64
src/arith.rs
64
src/arith.rs
|
@ -805,7 +805,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn rem_int() {
|
||||
use crate::types::I16F16;
|
||||
use crate::types::{I0F32, I16F16, I1F31};
|
||||
check_rem_int(-0x8000, -0x8000);
|
||||
check_rem_int(-0x8000, -0x7fff);
|
||||
check_rem_int(-0x8000, 0x7fff);
|
||||
|
@ -819,9 +819,71 @@ mod tests {
|
|||
check_rem_int(0x7fff, 0x7fff);
|
||||
check_rem_int(0x7fff, 0x8000);
|
||||
|
||||
fn i1(f: f32) -> I1F31 {
|
||||
I1F31::from_num(f)
|
||||
}
|
||||
fn i0(f: f32) -> I0F32 {
|
||||
I0F32::from_num(f)
|
||||
}
|
||||
|
||||
assert_eq!(I16F16::min_value() % -1, 0);
|
||||
assert_eq!(I16F16::min_value().checked_rem_int(-1).unwrap(), 0);
|
||||
assert_eq!(I16F16::min_value().rem_euclid_int(-1), 0);
|
||||
assert_eq!(I16F16::min_value().checked_rem_euclid_int(-1).unwrap(), 0);
|
||||
|
||||
assert_eq!(i1(-1.0) % 1, i1(0.0));
|
||||
assert_eq!(i1(-1.0).rem_euclid_int(1), i1(0.0));
|
||||
|
||||
assert_eq!(i1(-0.75) % 1, i1(-0.75));
|
||||
assert_eq!(i1(-0.75).rem_euclid_int(1), i1(0.25));
|
||||
|
||||
assert_eq!(i1(-0.5) % 1, i1(-0.5));
|
||||
assert_eq!(i1(-0.5).rem_euclid_int(1), i1(0.5));
|
||||
|
||||
assert_eq!(i1(-0.5) % 3, i1(-0.5));
|
||||
assert_eq!(i1(-0.5).checked_rem_euclid_int(3), None);
|
||||
assert_eq!(i1(-0.5).wrapping_rem_euclid_int(3), i1(0.5));
|
||||
assert_eq!(i1(-0.5).overflowing_rem_euclid_int(3), (i1(0.5), true));
|
||||
|
||||
assert_eq!(i1(-0.25) % 1, i1(-0.25));
|
||||
assert_eq!(i1(-0.25).rem_euclid_int(1), i1(0.75));
|
||||
|
||||
assert_eq!(i1(-0.25) % 3, i1(-0.25));
|
||||
assert_eq!(i1(-0.25).checked_rem_euclid_int(3), None);
|
||||
assert_eq!(i1(-0.25).wrapping_rem_euclid_int(3), i1(0.75));
|
||||
assert_eq!(i1(-0.25).overflowing_rem_euclid_int(3), (i1(0.75), true));
|
||||
|
||||
assert_eq!(i1(0.0) % 1, i1(0.0));
|
||||
assert_eq!(i1(0.0).rem_euclid_int(1), i1(0.0));
|
||||
|
||||
assert_eq!(i1(0.25) % 1, i1(0.25));
|
||||
assert_eq!(i1(0.25).rem_euclid_int(1), i1(0.25));
|
||||
|
||||
assert_eq!(i1(0.5) % 1, i1(0.5));
|
||||
assert_eq!(i1(0.5).rem_euclid_int(1), i1(0.5));
|
||||
|
||||
assert_eq!(i1(0.75) % 1, i1(0.75));
|
||||
assert_eq!(i1(0.75).rem_euclid_int(1), i1(0.75));
|
||||
|
||||
assert_eq!(i0(-0.5) % 1, i0(-0.5));
|
||||
assert_eq!(i0(-0.5).checked_rem_euclid_int(1), None);
|
||||
assert_eq!(i0(-0.5).wrapping_rem_euclid_int(1), i0(-0.5));
|
||||
assert_eq!(i0(-0.5).overflowing_rem_euclid_int(1), (i0(-0.5), true));
|
||||
|
||||
assert_eq!(i0(-0.375) % 1, i0(-0.375));
|
||||
assert_eq!(i0(-0.375).checked_rem_euclid_int(1), None);
|
||||
assert_eq!(i0(-0.375).wrapping_rem_euclid_int(1), i0(-0.375));
|
||||
assert_eq!(i0(-0.375).overflowing_rem_euclid_int(1), (i0(-0.375), true));
|
||||
|
||||
assert_eq!(i0(-0.25) % 1, i0(-0.25));
|
||||
assert_eq!(i0(-0.25).checked_rem_euclid_int(1), None);
|
||||
assert_eq!(i0(-0.25).wrapping_rem_euclid_int(1), i0(-0.25));
|
||||
assert_eq!(i0(-0.25).overflowing_rem_euclid_int(1), (i0(-0.25), true));
|
||||
|
||||
assert_eq!(i0(0.0) % 1, i0(0.0));
|
||||
assert_eq!(i0(0.0).rem_euclid_int(1), i0(0.0));
|
||||
|
||||
assert_eq!(i0(0.25) % 1, i0(0.25));
|
||||
assert_eq!(i0(0.25).rem_euclid_int(1), i0(0.25));
|
||||
}
|
||||
}
|
||||
|
|
31
src/lib.rs
31
src/lib.rs
|
@ -267,6 +267,7 @@ pub mod traits;
|
|||
pub mod types;
|
||||
mod wide_div;
|
||||
mod wrapping;
|
||||
|
||||
use crate::{
|
||||
arith::MulDivOverflow,
|
||||
from_str::FromStrRadix,
|
||||
|
@ -312,20 +313,22 @@ mod macros_frac;
|
|||
macro_rules! fixed {
|
||||
(
|
||||
$description:expr,
|
||||
$Fixed:ident($Inner:ty, $LeEqU:tt, $s_nbits:expr),
|
||||
$Fixed:ident($Inner:ty, $LeEqU:tt, $s_nbits:expr, $s_nbits_m4:expr),
|
||||
$nbytes:expr, $bytes_val:expr, $be_bytes:expr, $le_bytes:expr,
|
||||
$UInner:ty, $Signedness:tt
|
||||
) => {
|
||||
fixed! {
|
||||
$description,
|
||||
$Fixed[stringify!($Fixed)]($Inner[stringify!($Inner)], $LeEqU, $s_nbits),
|
||||
$Fixed[stringify!($Fixed)]($Inner[stringify!($Inner)], $LeEqU, $s_nbits, $s_nbits_m4),
|
||||
$nbytes, $bytes_val, $be_bytes, $le_bytes,
|
||||
$UInner, $Signedness
|
||||
}
|
||||
};
|
||||
(
|
||||
$description:expr,
|
||||
$Fixed:ident[$s_fixed:expr]($Inner:ty[$s_inner:expr], $LeEqU:tt, $s_nbits:expr),
|
||||
$Fixed:ident[$s_fixed:expr](
|
||||
$Inner:ty[$s_inner:expr], $LeEqU:tt, $s_nbits:expr, $s_nbits_m4:expr
|
||||
),
|
||||
$nbytes:expr, $bytes_val:expr, $be_bytes:expr, $le_bytes:expr,
|
||||
$UInner:ty, $Signedness:tt
|
||||
) => {
|
||||
|
@ -401,7 +404,7 @@ assert_eq!(two_point_75.to_string(), \"2.8\");
|
|||
// inherent methods that require Frac bounds, and cannot be const
|
||||
fixed_frac! {
|
||||
$description,
|
||||
$Fixed[$s_fixed]($Inner[$s_inner], $LeEqU, $s_nbits),
|
||||
$Fixed[$s_fixed]($Inner[$s_inner], $LeEqU, $s_nbits, $s_nbits_m4),
|
||||
$UInner, $Signedness
|
||||
}
|
||||
};
|
||||
|
@ -409,25 +412,25 @@ assert_eq!(two_point_75.to_string(), \"2.8\");
|
|||
|
||||
fixed! {
|
||||
"An eight-bit fixed-point unsigned",
|
||||
FixedU8(u8, LeEqU8, "8"),
|
||||
FixedU8(u8, LeEqU8, "8", "4"),
|
||||
1, "0x12", "[0x12]", "[0x12]",
|
||||
u8, Unsigned
|
||||
}
|
||||
fixed! {
|
||||
"A 16-bit fixed-point unsigned",
|
||||
FixedU16(u16, LeEqU16, "16"),
|
||||
FixedU16(u16, LeEqU16, "16", "12"),
|
||||
2, "0x1234", "[0x12, 0x34]", "[0x34, 0x12]",
|
||||
u16, Unsigned
|
||||
}
|
||||
fixed! {
|
||||
"A 32-bit fixed-point unsigned",
|
||||
FixedU32(u32, LeEqU32, "32"),
|
||||
FixedU32(u32, LeEqU32, "32", "28"),
|
||||
4, "0x1234_5678", "[0x12, 0x34, 0x56, 0x78]", "[0x78, 0x56, 0x34, 0x12]",
|
||||
u32, Unsigned
|
||||
}
|
||||
fixed! {
|
||||
"A 64-bit fixed-point unsigned",
|
||||
FixedU64(u64, LeEqU64, "64"),
|
||||
FixedU64(u64, LeEqU64, "64", "60"),
|
||||
8, "0x1234_5678_9ABC_DEF0",
|
||||
"[0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0]",
|
||||
"[0xF0, 0xDE, 0xBC, 0x9A, 0x78, 0x56, 0x34, 0x12]",
|
||||
|
@ -435,7 +438,7 @@ fixed! {
|
|||
}
|
||||
fixed! {
|
||||
"A 128-bit fixed-point unsigned",
|
||||
FixedU128(u128, LeEqU128, "128"),
|
||||
FixedU128(u128, LeEqU128, "128", "124"),
|
||||
16, "0x1234_5678_9ABC_DEF0_1234_5678_9ABC_DEF0",
|
||||
"[0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0, \
|
||||
0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0]",
|
||||
|
@ -445,25 +448,25 @@ fixed! {
|
|||
}
|
||||
fixed! {
|
||||
"An eight-bit fixed-point signed",
|
||||
FixedI8(i8, LeEqU8, "8"),
|
||||
FixedI8(i8, LeEqU8, "8", "4"),
|
||||
1, "0x12", "[0x12]", "[0x12]",
|
||||
u8, Signed
|
||||
}
|
||||
fixed! {
|
||||
"A 16-bit fixed-point signed",
|
||||
FixedI16(i16, LeEqU16, "16"),
|
||||
FixedI16(i16, LeEqU16, "16", "12"),
|
||||
2, "0x1234", "[0x12, 0x34]", "[0x34, 0x12]",
|
||||
u16, Signed
|
||||
}
|
||||
fixed! {
|
||||
"A 32-bit fixed-point signed",
|
||||
FixedI32(i32, LeEqU32, "32"),
|
||||
FixedI32(i32, LeEqU32, "32", "28"),
|
||||
4, "0x1234_5678", "[0x12, 0x34, 0x56, 0x78]", "[0x78, 0x56, 0x34, 0x12]",
|
||||
u32, Signed
|
||||
}
|
||||
fixed! {
|
||||
"A 64-bit fixed-point signed",
|
||||
FixedI64(i64, LeEqU64, "64"),
|
||||
FixedI64(i64, LeEqU64, "64", "60"),
|
||||
8, "0x1234_5678_9ABC_DEF0",
|
||||
"[0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0]",
|
||||
"[0xF0, 0xDE, 0xBC, 0x9A, 0x78, 0x56, 0x34, 0x12]",
|
||||
|
@ -471,7 +474,7 @@ fixed! {
|
|||
}
|
||||
fixed! {
|
||||
"A 128-bit fixed-point signed",
|
||||
FixedI128(i128, LeEqU128, "128"),
|
||||
FixedI128(i128, LeEqU128, "128", "124"),
|
||||
16, "0x1234_5678_9ABC_DEF0_1234_5678_9ABC_DEF0",
|
||||
"[0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0, \
|
||||
0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0]",
|
||||
|
|
|
@ -16,7 +16,9 @@
|
|||
macro_rules! fixed_frac {
|
||||
(
|
||||
$description:expr,
|
||||
$Fixed:ident[$s_fixed:expr]($Inner:ty[$s_inner:expr], $LeEqU:tt, $s_nbits:expr),
|
||||
$Fixed:ident[$s_fixed:expr](
|
||||
$Inner:ty[$s_inner:expr], $LeEqU:tt, $s_nbits:expr, $s_nbits_m4:expr
|
||||
),
|
||||
$UInner:ty, $Signedness:tt
|
||||
) => {
|
||||
impl<Frac: $LeEqU> $Fixed<Frac> {
|
||||
|
@ -257,7 +259,9 @@ assert_eq!(Fix::from_num(7.5).rem_euclid_int(2), Fix::from_num(1.5));
|
|||
";
|
||||
#[inline]
|
||||
pub fn rem_euclid_int(self, rhs: $Inner) -> $Fixed<Frac> {
|
||||
self.checked_rem_euclid_int(rhs).expect("division by zero")
|
||||
let (ans, overflow) = self.overflowing_rem_euclid_int(rhs);
|
||||
debug_assert!(!overflow, "overflow");
|
||||
ans
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -375,12 +379,15 @@ assert_eq!(Fix::from_num(3.75).checked_rem_int(0), None);
|
|||
pub fn checked_rem_int(self, rhs: $Inner) -> Option<$Fixed<Frac>> {
|
||||
// Overflow converting rhs to $Fixed<Frac> means that either
|
||||
// * |rhs| > |self|, and so remainder is self, or
|
||||
// * self is signed min, and the value of rhs is -self, so remainder is 0.
|
||||
// * self is signed min with at least one integer bit,
|
||||
// and the value of rhs is -self, so remainder is 0.
|
||||
match Self::checked_from_num(rhs) {
|
||||
Some(fixed_rhs) => self.checked_rem(fixed_rhs),
|
||||
None => Some(if_signed_unsigned!(
|
||||
$Signedness,
|
||||
if self.to_num::<$Inner>().wrapping_abs() == rhs {
|
||||
if self == Self::min_value()
|
||||
&& (Self::INT_NBITS > 0 && rhs == 1 << (Self::INT_NBITS - 1))
|
||||
{
|
||||
Self::from_bits(0)
|
||||
} else {
|
||||
self
|
||||
|
@ -437,19 +444,26 @@ assert_eq!(Fix::from_num(7.5).checked_div_euclid_int(0), None);
|
|||
|
||||
comment! {
|
||||
"Checked remainder for Euclidean division by an integer.
|
||||
Returns the remainder, or [`None`] if the divisor is zero.
|
||||
Returns the remainder, or [`None`] if the divisor is zero",
|
||||
if_signed_else_empty_str! {
|
||||
$Signedness,
|
||||
" or if the remainder results in overflow",
|
||||
},
|
||||
".
|
||||
|
||||
# Examples
|
||||
|
||||
```rust
|
||||
use fixed::{types::extra::U4, ", $s_fixed, "};
|
||||
type Fix = ", $s_fixed, "<U4>;
|
||||
use fixed::{types::extra::U", $s_nbits_m4, ", ", $s_fixed, "};
|
||||
type Fix = ", $s_fixed, "<U", $s_nbits_m4, ">;
|
||||
assert_eq!(Fix::from_num(7.5).checked_rem_euclid_int(2), Some(Fix::from_num(1.5)));
|
||||
assert_eq!(Fix::from_num(7.5).checked_rem_euclid_int(0), None);
|
||||
",
|
||||
if_signed_else_empty_str! {
|
||||
$Signedness,
|
||||
"assert_eq!(Fix::from_num(-7.5).checked_rem_euclid_int(2), Some(Fix::from_num(0.5)));
|
||||
// −8 ≤ Fix < 8, so the answer 12.5 overflows
|
||||
assert_eq!(Fix::from_num(-7.5).checked_rem_euclid_int(20), None);
|
||||
",
|
||||
},
|
||||
"```
|
||||
|
@ -458,18 +472,31 @@ assert_eq!(Fix::from_num(7.5).checked_rem_euclid_int(0), None);
|
|||
";
|
||||
#[inline]
|
||||
pub fn checked_rem_euclid_int(self, rhs: $Inner) -> Option<$Fixed<Frac>> {
|
||||
// For signed rem_euclid_int, rhs can be made
|
||||
// negative without changing result.
|
||||
// Then, overflow converting rhs to $Fixed<Frac> means
|
||||
// that |rhs| > |self|, and so remainder is self.
|
||||
let prep_rhs = if_signed_unsigned!(
|
||||
$Signedness,
|
||||
rhs.wrapping_abs().wrapping_neg(),
|
||||
rhs,
|
||||
);
|
||||
match Self::checked_from_num(prep_rhs) {
|
||||
Some(fixed_rhs) => self.checked_rem_euclid(fixed_rhs),
|
||||
None => Some(self),
|
||||
if_signed! {
|
||||
$Signedness;
|
||||
let rem = self.checked_rem_int(rhs)?;
|
||||
if rem >= 0 {
|
||||
return Some(rem);
|
||||
}
|
||||
// Work in unsigned.
|
||||
// Answer required is |rhs| - |rem|, but rhs is int, rem is fixed.
|
||||
// INT_NBITS == 0 is a special case, as fraction can be negative.
|
||||
if Self::INT_NBITS == 0 {
|
||||
// -0.5 <= rem < 0, so euclidean remainder is in the range
|
||||
// 0.5 <= answer < 1, which does not fit.
|
||||
return None;
|
||||
}
|
||||
let rhs_abs = rhs.wrapping_abs() as $UInner;
|
||||
let remb = rem.to_bits();
|
||||
let remb_abs = remb.wrapping_neg() as $UInner;
|
||||
let rem_int_abs = remb_abs >> Self::FRAC_NBITS;
|
||||
let rem_frac = remb & Self::FRAC_MASK;
|
||||
let ans_int = rhs_abs - rem_int_abs - if rem_frac > 0 { 1 } else { 0 };
|
||||
Self::checked_from_num(ans_int).map(|x| x | Self::from_bits(rem_frac))
|
||||
}
|
||||
if_unsigned! {
|
||||
$Signedness;
|
||||
self.checked_rem_int(rhs)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -682,6 +709,46 @@ assert_eq!(Fix::min_value().wrapping_div_euclid_int(-1), wrapped);
|
|||
}
|
||||
}
|
||||
|
||||
comment! {
|
||||
"Wrapping remainder for Euclidean division by an integer. Returns the remainder",
|
||||
if_signed_unsigned! {
|
||||
$Signedness,
|
||||
", wrapping on overflow.
|
||||
|
||||
Note that while remainder for Euclidean division cannot be negative,
|
||||
the wrapped value can be negative.",
|
||||
".
|
||||
|
||||
Can never overflow for unsigned values.",
|
||||
},
|
||||
"
|
||||
|
||||
# Panics
|
||||
|
||||
Panics if the divisor is zero.
|
||||
|
||||
# Examples
|
||||
|
||||
```rust
|
||||
use fixed::{types::extra::U", $s_nbits_m4, ", ", $s_fixed, "};
|
||||
type Fix = ", $s_fixed, "<U", $s_nbits_m4, ">;
|
||||
assert_eq!(Fix::from_num(7.5).wrapping_rem_euclid_int(2), Fix::from_num(1.5));
|
||||
",
|
||||
if_signed_else_empty_str! {
|
||||
$Signedness,
|
||||
"assert_eq!(Fix::from_num(-7.5).wrapping_rem_euclid_int(2), Fix::from_num(0.5));
|
||||
// −8 ≤ Fix < 8, so the answer 12.5 wraps to −3.5
|
||||
assert_eq!(Fix::from_num(-7.5).wrapping_rem_euclid_int(20), Fix::from_num(-3.5));
|
||||
",
|
||||
},
|
||||
"```
|
||||
";
|
||||
#[inline]
|
||||
pub fn wrapping_rem_euclid_int(self, rhs: $Inner) -> $Fixed<Frac> {
|
||||
self.overflowing_rem_euclid_int(rhs).0
|
||||
}
|
||||
}
|
||||
|
||||
comment! {
|
||||
"Overflowing multiplication.
|
||||
|
||||
|
@ -854,6 +921,76 @@ assert_eq!(Fix::min_value().overflowing_div_euclid_int(-1), (wrapped, true));
|
|||
}
|
||||
}
|
||||
|
||||
comment! {
|
||||
"Remainder for Euclidean division by an integer.
|
||||
|
||||
Returns a [tuple] of the remainder and ",
|
||||
if_signed_unsigned! {
|
||||
$Signedness,
|
||||
"a [`bool`] indicating whether an overflow has
|
||||
occurred. On overflow, the wrapped value is returned.
|
||||
|
||||
Note that while remainder for Euclidean division cannot be negative,
|
||||
the wrapped value can be negative.",
|
||||
"[`false`][`bool`], as this can never overflow for unsigned values.",
|
||||
},
|
||||
"
|
||||
|
||||
# Panics
|
||||
|
||||
Panics if the divisor is zero.
|
||||
|
||||
# Examples
|
||||
|
||||
```rust
|
||||
use fixed::{types::extra::U", $s_nbits_m4, ", ", $s_fixed, "};
|
||||
type Fix = ", $s_fixed, "<U", $s_nbits_m4, ">;
|
||||
assert_eq!(Fix::from_num(7.5).overflowing_rem_euclid_int(2), (Fix::from_num(1.5), false));
|
||||
",
|
||||
if_signed_else_empty_str! {
|
||||
$Signedness,
|
||||
"assert_eq!(Fix::from_num(-7.5).overflowing_rem_euclid_int(2), (Fix::from_num(0.5), false));
|
||||
// −8 ≤ Fix < 8, so the answer 12.5 wraps to −3.5
|
||||
assert_eq!(Fix::from_num(-7.5).overflowing_rem_euclid_int(20), (Fix::from_num(-3.5), true));
|
||||
",
|
||||
},
|
||||
"```
|
||||
|
||||
[`bool`]: https://doc.rust-lang.org/nightly/std/primitive.bool.html
|
||||
[tuple]: https://doc.rust-lang.org/nightly/std/primitive.tuple.html
|
||||
";
|
||||
#[inline]
|
||||
pub fn overflowing_rem_euclid_int(self, rhs: $Inner) -> ($Fixed<Frac>, bool) {
|
||||
if_signed! {
|
||||
$Signedness;
|
||||
let rem = self % rhs;
|
||||
if rem >= 0 {
|
||||
return (rem, false);
|
||||
}
|
||||
// Work in unsigned.
|
||||
// Answer required is |rhs| - |rem|, but rhs is int, rem is fixed.
|
||||
// INT_NBITS == 0 is a special case, as fraction can be negative.
|
||||
if Self::INT_NBITS == 0 {
|
||||
// -0.5 <= rem < 0, so euclidean remainder is in the range
|
||||
// 0.5 <= answer < 1, which does not fit.
|
||||
return (rem, true);
|
||||
}
|
||||
let rhs_abs = rhs.wrapping_abs() as $UInner;
|
||||
let remb = rem.to_bits();
|
||||
let remb_abs = remb.wrapping_neg() as $UInner;
|
||||
let rem_int_abs = remb_abs >> Self::FRAC_NBITS;
|
||||
let rem_frac = remb & Self::FRAC_MASK;
|
||||
let ans_int = rhs_abs - rem_int_abs - if rem_frac > 0 { 1 } else { 0 };
|
||||
let (ans, overflow) = Self::overflowing_from_num(ans_int);
|
||||
(ans | Self::from_bits(rem_frac), overflow)
|
||||
}
|
||||
if_unsigned! {
|
||||
$Signedness;
|
||||
(self % rhs, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Remainder for division by an integer.
|
||||
///
|
||||
/// # Panics
|
||||
|
@ -875,28 +1012,6 @@ assert_eq!(Fix::min_value().overflowing_div_euclid_int(-1), (wrapped, true));
|
|||
pub fn overflowing_rem_int(self, rhs: $Inner) -> ($Fixed<Frac>, bool) {
|
||||
(self % rhs, false)
|
||||
}
|
||||
|
||||
/// Remainder for Euclidean division by an integer.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the divisor is zero.
|
||||
#[deprecated(since = "0.5.3", note = "cannot overflow, use `rem_euclid_int` instead")]
|
||||
#[inline]
|
||||
pub fn wrapping_rem_euclid_int(self, rhs: $Inner) -> $Fixed<Frac> {
|
||||
self.rem_euclid_int(rhs)
|
||||
}
|
||||
|
||||
/// Remainder for Euclidean division by an integer.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the divisor is zero.
|
||||
#[deprecated(since = "0.5.3", note = "cannot overflow, use `rem_euclid_int` instead")]
|
||||
#[inline]
|
||||
pub fn overflowing_rem_euclid_int(self, rhs: $Inner) -> ($Fixed<Frac>, bool) {
|
||||
(self.rem_euclid_int(rhs), false)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -689,9 +689,9 @@ where
|
|||
/// [`None`]: https://doc.rust-lang.org/nightly/core/option/enum.Option.html#variant.None
|
||||
fn checked_div_euclid_int(self, rhs: Self::Bits) -> Option<Self>;
|
||||
|
||||
/// Checked fixed-point remainder for Euclidean division by an
|
||||
/// integer. Returns the remainder, or [`None`] if the divisor is
|
||||
/// zero.
|
||||
/// Checked remainder for Euclidean division by an integer.
|
||||
/// Returns the remainder, or [`None`] if the divisor is zero or
|
||||
/// if the remainder results in overflow.
|
||||
///
|
||||
/// [`None`]: https://doc.rust-lang.org/nightly/core/option/enum.Option.html#variant.None
|
||||
fn checked_rem_euclid_int(self, rhs: Self::Bits) -> Option<Self>;
|
||||
|
@ -785,6 +785,14 @@ where
|
|||
/// Panics if the divisor is zero.
|
||||
fn wrapping_div_euclid_int(self, rhs: Self::Bits) -> Self;
|
||||
|
||||
/// Wrapping remainder for Euclidean division by an integer.
|
||||
/// Returns the remainder, wrapping on overflow.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the divisor is zero.
|
||||
fn wrapping_rem_euclid_int(self, rhs: Self::Bits) -> Self;
|
||||
|
||||
/// Wrapping shift left. Wraps `rhs` if `rhs` ≥ the number of
|
||||
/// bits, then shifts and returns the number.
|
||||
fn wrapping_shl(self, rhs: u32) -> Self;
|
||||
|
@ -899,6 +907,20 @@ where
|
|||
/// [tuple]: https://doc.rust-lang.org/nightly/std/primitive.tuple.html
|
||||
fn overflowing_div_euclid_int(self, rhs: Self::Bits) -> (Self, bool);
|
||||
|
||||
/// Overflowing remainder for Euclidean division by an integer.
|
||||
///
|
||||
/// Returns a [tuple] of the remainder and a [`bool`], indicating
|
||||
/// whether an overflow has occurred. On overflow, the wrapped
|
||||
/// value is returned.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the divisor is zero.
|
||||
///
|
||||
/// [`bool`]: https://doc.rust-lang.org/nightly/std/primitive.bool.html
|
||||
/// [tuple]: https://doc.rust-lang.org/nightly/std/primitive.tuple.html
|
||||
fn overflowing_rem_euclid_int(self, rhs: Self::Bits) -> (Self, bool);
|
||||
|
||||
/// Overflowing shift left.
|
||||
///
|
||||
/// Returns a [tuple] of the shifted value and a [`bool`],
|
||||
|
@ -944,32 +966,6 @@ where
|
|||
fn overflowing_rem_int(self, rhs: Self::Bits) -> (Self, bool) {
|
||||
(self % rhs, false)
|
||||
}
|
||||
|
||||
/// Remainder for Euclidean division by an integer.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the divisor is zero.
|
||||
#[deprecated(
|
||||
since = "0.5.3",
|
||||
note = "cannot overflow, use `rem_euclid_int` instead"
|
||||
)]
|
||||
fn wrapping_rem_euclid_int(self, rhs: Self::Bits) -> Self {
|
||||
self.rem_euclid_int(rhs)
|
||||
}
|
||||
|
||||
/// Remainder for Euclidean division by an integer.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the divisor is zero.
|
||||
#[deprecated(
|
||||
since = "0.5.3",
|
||||
note = "cannot overflow, use `rem_euclid_int` instead"
|
||||
)]
|
||||
fn overflowing_rem_euclid_int(self, rhs: Self::Bits) -> (Self, bool) {
|
||||
(self.rem_euclid_int(rhs), false)
|
||||
}
|
||||
}
|
||||
|
||||
/// This trait provides methods common to all signed fixed-point numbers.
|
||||
|
@ -1616,6 +1612,7 @@ macro_rules! impl_fixed {
|
|||
trait_delegate! { fn wrapping_mul_int(self, rhs: Self::Bits) -> Self }
|
||||
trait_delegate! { fn wrapping_div_int(self, rhs: Self::Bits) -> Self }
|
||||
trait_delegate! { fn wrapping_div_euclid_int(self, rhs: Self::Bits) -> Self }
|
||||
trait_delegate! { fn wrapping_rem_euclid_int(self, rhs: Self::Bits) -> Self }
|
||||
trait_delegate! { fn wrapping_shl(self, rhs: u32) -> Self }
|
||||
trait_delegate! { fn wrapping_shr(self, rhs: u32) -> Self }
|
||||
trait_delegate! { fn overflowing_neg(self) -> (Self, bool) }
|
||||
|
@ -1627,6 +1624,7 @@ macro_rules! impl_fixed {
|
|||
trait_delegate! { fn overflowing_mul_int(self, rhs: Self::Bits) -> (Self, bool) }
|
||||
trait_delegate! { fn overflowing_div_int(self, rhs: Self::Bits) -> (Self, bool) }
|
||||
trait_delegate! { fn overflowing_div_euclid_int(self, rhs: Self::Bits) -> (Self, bool) }
|
||||
trait_delegate! { fn overflowing_rem_euclid_int(self, rhs: Self::Bits) -> (Self, bool) }
|
||||
trait_delegate! { fn overflowing_shl(self, rhs: u32) -> (Self, bool) }
|
||||
trait_delegate! { fn overflowing_shr(self, rhs: u32) -> (Self, bool) }
|
||||
}
|
||||
|
|
|
@ -603,7 +603,7 @@ impl<F: Fixed> Wrapping<F> {
|
|||
/// ```
|
||||
#[inline]
|
||||
pub fn rem_euclid_int(self, divisor: F::Bits) -> Wrapping<F> {
|
||||
Wrapping(self.0.rem_euclid_int(divisor))
|
||||
Wrapping(self.0.wrapping_rem_euclid_int(divisor))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue