fix incorrect overflow handling for rem_euclid_int

This commit is contained in:
Trevor Spiteri 2020-02-21 15:39:02 +01:00
parent e8f5a8238e
commit e0f3eb77b8
7 changed files with 282 additions and 86 deletions

View File

@ -74,6 +74,11 @@ The conversions supported cover the following cases.
## Whats new ## Whats 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) ### Version 0.5.3 news (2020-02-13)
* Bug fix: [`round_to_zero`] was returning incorrect results for * 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: * The following methods were added to the [`Wrapping`] wrapper:
* [`div_euclid`][wde], [`rem_euclid`][wre] * [`div_euclid`][wde], [`rem_euclid`][wre]
* [`div_euclid_int`][wdei], [`rem_euclid_int`][wrei] * [`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) ### 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 [`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 [`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_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 [`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 [`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 [`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_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 [issue 13]: https://gitlab.com/tspiteri/fixed/issues/13
[wde]: https://docs.rs/fixed/0.5.3/fixed/struct.Wrapping.html#method.div_euclid [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 [wdei]: https://docs.rs/fixed/0.5.3/fixed/struct.Wrapping.html#method.div_euclid_int

View File

@ -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 copyright notice and this notice are preserved. This file is offered
as-is, without any warranty. --> 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) 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: * The following methods were added to the `Wrapping` wrapper:
* `div_euclid`, `rem_euclid` * `div_euclid`, `rem_euclid`
* `div_euclid_int`, `rem_euclid_int` * `div_euclid_int`, `rem_euclid_int`
* The following methods were deprecated:
* `wrapping_rem_int`, `overflowing_rem_int`
Version 0.5.2 (2020-02-02) Version 0.5.2 (2020-02-02)
========================== ==========================

View File

@ -805,7 +805,7 @@ mod tests {
#[test] #[test]
fn rem_int() { fn rem_int() {
use crate::types::I16F16; use crate::types::{I0F32, I16F16, I1F31};
check_rem_int(-0x8000, -0x8000); check_rem_int(-0x8000, -0x8000);
check_rem_int(-0x8000, -0x7fff); check_rem_int(-0x8000, -0x7fff);
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, 0x7fff);
check_rem_int(0x7fff, 0x8000); 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() % -1, 0);
assert_eq!(I16F16::min_value().checked_rem_int(-1).unwrap(), 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().rem_euclid_int(-1), 0);
assert_eq!(I16F16::min_value().checked_rem_euclid_int(-1).unwrap(), 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));
} }
} }

View File

@ -267,6 +267,7 @@ pub mod traits;
pub mod types; pub mod types;
mod wide_div; mod wide_div;
mod wrapping; mod wrapping;
use crate::{ use crate::{
arith::MulDivOverflow, arith::MulDivOverflow,
from_str::FromStrRadix, from_str::FromStrRadix,
@ -312,20 +313,22 @@ mod macros_frac;
macro_rules! fixed { macro_rules! fixed {
( (
$description:expr, $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, $nbytes:expr, $bytes_val:expr, $be_bytes:expr, $le_bytes:expr,
$UInner:ty, $Signedness:tt $UInner:ty, $Signedness:tt
) => { ) => {
fixed! { fixed! {
$description, $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, $nbytes, $bytes_val, $be_bytes, $le_bytes,
$UInner, $Signedness $UInner, $Signedness
} }
}; };
( (
$description:expr, $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, $nbytes:expr, $bytes_val:expr, $be_bytes:expr, $le_bytes:expr,
$UInner:ty, $Signedness:tt $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 // inherent methods that require Frac bounds, and cannot be const
fixed_frac! { fixed_frac! {
$description, $description,
$Fixed[$s_fixed]($Inner[$s_inner], $LeEqU, $s_nbits), $Fixed[$s_fixed]($Inner[$s_inner], $LeEqU, $s_nbits, $s_nbits_m4),
$UInner, $Signedness $UInner, $Signedness
} }
}; };
@ -409,25 +412,25 @@ assert_eq!(two_point_75.to_string(), \"2.8\");
fixed! { fixed! {
"An eight-bit fixed-point unsigned", "An eight-bit fixed-point unsigned",
FixedU8(u8, LeEqU8, "8"), FixedU8(u8, LeEqU8, "8", "4"),
1, "0x12", "[0x12]", "[0x12]", 1, "0x12", "[0x12]", "[0x12]",
u8, Unsigned u8, Unsigned
} }
fixed! { fixed! {
"A 16-bit fixed-point unsigned", "A 16-bit fixed-point unsigned",
FixedU16(u16, LeEqU16, "16"), FixedU16(u16, LeEqU16, "16", "12"),
2, "0x1234", "[0x12, 0x34]", "[0x34, 0x12]", 2, "0x1234", "[0x12, 0x34]", "[0x34, 0x12]",
u16, Unsigned u16, Unsigned
} }
fixed! { fixed! {
"A 32-bit fixed-point unsigned", "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]", 4, "0x1234_5678", "[0x12, 0x34, 0x56, 0x78]", "[0x78, 0x56, 0x34, 0x12]",
u32, Unsigned u32, Unsigned
} }
fixed! { fixed! {
"A 64-bit fixed-point unsigned", "A 64-bit fixed-point unsigned",
FixedU64(u64, LeEqU64, "64"), FixedU64(u64, LeEqU64, "64", "60"),
8, "0x1234_5678_9ABC_DEF0", 8, "0x1234_5678_9ABC_DEF0",
"[0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0]", "[0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0]",
"[0xF0, 0xDE, 0xBC, 0x9A, 0x78, 0x56, 0x34, 0x12]", "[0xF0, 0xDE, 0xBC, 0x9A, 0x78, 0x56, 0x34, 0x12]",
@ -435,7 +438,7 @@ fixed! {
} }
fixed! { fixed! {
"A 128-bit fixed-point unsigned", "A 128-bit fixed-point unsigned",
FixedU128(u128, LeEqU128, "128"), FixedU128(u128, LeEqU128, "128", "124"),
16, "0x1234_5678_9ABC_DEF0_1234_5678_9ABC_DEF0", 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, \
0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0]", 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0]",
@ -445,25 +448,25 @@ fixed! {
} }
fixed! { fixed! {
"An eight-bit fixed-point signed", "An eight-bit fixed-point signed",
FixedI8(i8, LeEqU8, "8"), FixedI8(i8, LeEqU8, "8", "4"),
1, "0x12", "[0x12]", "[0x12]", 1, "0x12", "[0x12]", "[0x12]",
u8, Signed u8, Signed
} }
fixed! { fixed! {
"A 16-bit fixed-point signed", "A 16-bit fixed-point signed",
FixedI16(i16, LeEqU16, "16"), FixedI16(i16, LeEqU16, "16", "12"),
2, "0x1234", "[0x12, 0x34]", "[0x34, 0x12]", 2, "0x1234", "[0x12, 0x34]", "[0x34, 0x12]",
u16, Signed u16, Signed
} }
fixed! { fixed! {
"A 32-bit fixed-point signed", "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]", 4, "0x1234_5678", "[0x12, 0x34, 0x56, 0x78]", "[0x78, 0x56, 0x34, 0x12]",
u32, Signed u32, Signed
} }
fixed! { fixed! {
"A 64-bit fixed-point signed", "A 64-bit fixed-point signed",
FixedI64(i64, LeEqU64, "64"), FixedI64(i64, LeEqU64, "64", "60"),
8, "0x1234_5678_9ABC_DEF0", 8, "0x1234_5678_9ABC_DEF0",
"[0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0]", "[0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0]",
"[0xF0, 0xDE, 0xBC, 0x9A, 0x78, 0x56, 0x34, 0x12]", "[0xF0, 0xDE, 0xBC, 0x9A, 0x78, 0x56, 0x34, 0x12]",
@ -471,7 +474,7 @@ fixed! {
} }
fixed! { fixed! {
"A 128-bit fixed-point signed", "A 128-bit fixed-point signed",
FixedI128(i128, LeEqU128, "128"), FixedI128(i128, LeEqU128, "128", "124"),
16, "0x1234_5678_9ABC_DEF0_1234_5678_9ABC_DEF0", 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, \
0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0]", 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0]",

View File

@ -16,7 +16,9 @@
macro_rules! fixed_frac { macro_rules! fixed_frac {
( (
$description:expr, $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 $UInner:ty, $Signedness:tt
) => { ) => {
impl<Frac: $LeEqU> $Fixed<Frac> { 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] #[inline]
pub fn rem_euclid_int(self, rhs: $Inner) -> $Fixed<Frac> { 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>> { pub fn checked_rem_int(self, rhs: $Inner) -> Option<$Fixed<Frac>> {
// Overflow converting rhs to $Fixed<Frac> means that either // Overflow converting rhs to $Fixed<Frac> means that either
// * |rhs| > |self|, and so remainder is self, or // * |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) { match Self::checked_from_num(rhs) {
Some(fixed_rhs) => self.checked_rem(fixed_rhs), Some(fixed_rhs) => self.checked_rem(fixed_rhs),
None => Some(if_signed_unsigned!( None => Some(if_signed_unsigned!(
$Signedness, $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) Self::from_bits(0)
} else { } else {
self self
@ -437,19 +444,26 @@ assert_eq!(Fix::from_num(7.5).checked_div_euclid_int(0), None);
comment! { comment! {
"Checked remainder for Euclidean division by an integer. "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 # Examples
```rust ```rust
use fixed::{types::extra::U4, ", $s_fixed, "}; use fixed::{types::extra::U", $s_nbits_m4, ", ", $s_fixed, "};
type Fix = ", $s_fixed, "<U4>; 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(2), Some(Fix::from_num(1.5)));
assert_eq!(Fix::from_num(7.5).checked_rem_euclid_int(0), None); assert_eq!(Fix::from_num(7.5).checked_rem_euclid_int(0), None);
", ",
if_signed_else_empty_str! { if_signed_else_empty_str! {
$Signedness, $Signedness,
"assert_eq!(Fix::from_num(-7.5).checked_rem_euclid_int(2), Some(Fix::from_num(0.5))); "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] #[inline]
pub fn checked_rem_euclid_int(self, rhs: $Inner) -> Option<$Fixed<Frac>> { pub fn checked_rem_euclid_int(self, rhs: $Inner) -> Option<$Fixed<Frac>> {
// For signed rem_euclid_int, rhs can be made if_signed! {
// negative without changing result. $Signedness;
// Then, overflow converting rhs to $Fixed<Frac> means let rem = self.checked_rem_int(rhs)?;
// that |rhs| > |self|, and so remainder is self. if rem >= 0 {
let prep_rhs = if_signed_unsigned!( return Some(rem);
$Signedness, }
rhs.wrapping_abs().wrapping_neg(), // Work in unsigned.
rhs, // Answer required is |rhs| - |rem|, but rhs is int, rem is fixed.
); // INT_NBITS == 0 is a special case, as fraction can be negative.
match Self::checked_from_num(prep_rhs) { if Self::INT_NBITS == 0 {
Some(fixed_rhs) => self.checked_rem_euclid(fixed_rhs), // -0.5 <= rem < 0, so euclidean remainder is in the range
None => Some(self), // 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! { comment! {
"Overflowing multiplication. "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. /// Remainder for division by an integer.
/// ///
/// # Panics /// # 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) { pub fn overflowing_rem_int(self, rhs: $Inner) -> ($Fixed<Frac>, bool) {
(self % rhs, false) (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)
}
} }
}; };
} }

View File

@ -689,9 +689,9 @@ where
/// [`None`]: https://doc.rust-lang.org/nightly/core/option/enum.Option.html#variant.None /// [`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>; fn checked_div_euclid_int(self, rhs: Self::Bits) -> Option<Self>;
/// Checked fixed-point remainder for Euclidean division by an /// Checked remainder for Euclidean division by an integer.
/// integer. Returns the remainder, or [`None`] if the divisor is /// Returns the remainder, or [`None`] if the divisor is zero or
/// zero. /// if the remainder results in overflow.
/// ///
/// [`None`]: https://doc.rust-lang.org/nightly/core/option/enum.Option.html#variant.None /// [`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>; fn checked_rem_euclid_int(self, rhs: Self::Bits) -> Option<Self>;
@ -785,6 +785,14 @@ where
/// Panics if the divisor is zero. /// Panics if the divisor is zero.
fn wrapping_div_euclid_int(self, rhs: Self::Bits) -> Self; 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 /// Wrapping shift left. Wraps `rhs` if `rhs` ≥ the number of
/// bits, then shifts and returns the number. /// bits, then shifts and returns the number.
fn wrapping_shl(self, rhs: u32) -> Self; fn wrapping_shl(self, rhs: u32) -> Self;
@ -899,6 +907,20 @@ where
/// [tuple]: https://doc.rust-lang.org/nightly/std/primitive.tuple.html /// [tuple]: https://doc.rust-lang.org/nightly/std/primitive.tuple.html
fn overflowing_div_euclid_int(self, rhs: Self::Bits) -> (Self, bool); 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. /// Overflowing shift left.
/// ///
/// Returns a [tuple] of the shifted value and a [`bool`], /// 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) { fn overflowing_rem_int(self, rhs: Self::Bits) -> (Self, bool) {
(self % rhs, false) (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. /// 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_mul_int(self, rhs: Self::Bits) -> Self }
trait_delegate! { fn wrapping_div_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_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_shl(self, rhs: u32) -> Self }
trait_delegate! { fn wrapping_shr(self, rhs: u32) -> Self } trait_delegate! { fn wrapping_shr(self, rhs: u32) -> Self }
trait_delegate! { fn overflowing_neg(self) -> (Self, bool) } 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_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_int(self, rhs: Self::Bits) -> (Self, bool) }
trait_delegate! { fn overflowing_div_euclid_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_shl(self, rhs: u32) -> (Self, bool) }
trait_delegate! { fn overflowing_shr(self, rhs: u32) -> (Self, bool) } trait_delegate! { fn overflowing_shr(self, rhs: u32) -> (Self, bool) }
} }

View File

@ -603,7 +603,7 @@ impl<F: Fixed> Wrapping<F> {
/// ``` /// ```
#[inline] #[inline]
pub fn rem_euclid_int(self, divisor: F::Bits) -> Wrapping<F> { 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))
} }
} }