add mul_add methods
This commit is contained in:
parent
b53fb709bf
commit
a3d947776d
11
README.md
11
README.md
|
@ -80,10 +80,21 @@ The conversions supported cover the following cases.
|
|||
|
||||
### Version 1.3.0 news (unreleased)
|
||||
|
||||
* The following methods were added to all fixed-point types and to
|
||||
the `Fixed` trait:
|
||||
* [`mul_add`][f-ma-1-3], [`checked_mul_add`][f-cma-1-3],
|
||||
[`saturating_mul_add`][f-sma-1-3],
|
||||
[`wrapping_mul_add`][f-wma-1-3],
|
||||
[`overflowing_mul_add`][f-oma-1-3]
|
||||
* The new experimental feature [`unwrapped`][feat-un-1-3] was added,
|
||||
providing arithmetic methods that panic on overflow even when
|
||||
debug assertions are disabled.
|
||||
|
||||
[f-cma-1-3]: https://tspiteri.gitlab.io/fixed/dev/fixed/struct.FixedI32.html#method.checked_mul_add
|
||||
[f-ma-1-3]: https://tspiteri.gitlab.io/fixed/dev/fixed/struct.FixedI32.html#method.mul_add
|
||||
[f-oma-1-3]: https://tspiteri.gitlab.io/fixed/dev/fixed/struct.FixedI32.html#method.overflowing_mul_add
|
||||
[f-sma-1-3]: https://tspiteri.gitlab.io/fixed/dev/fixed/struct.FixedI32.html#method.saturating_mul_add
|
||||
[f-wma-1-3]: https://tspiteri.gitlab.io/fixed/dev/fixed/struct.FixedI32.html#method.wrapping_mul_add
|
||||
[feat-un-1-3]: https://tspiteri.gitlab.io/fixed/dev/fixed/#experimental-optional-features
|
||||
|
||||
### Version 1.2.0 news (2020-09-02)
|
||||
|
|
11
RELEASES.md
11
RELEASES.md
|
@ -8,10 +8,21 @@ as-is, without any warranty. -->
|
|||
Version 1.3.0 (unreleased)
|
||||
==========================
|
||||
|
||||
* The following methods were added to all fixed-point types and to
|
||||
the `Fixed` trait:
|
||||
* [`mul_add`][f-ma-1-3], [`checked_mul_add`][f-cma-1-3],
|
||||
[`saturating_mul_add`][f-sma-1-3],
|
||||
[`wrapping_mul_add`][f-wma-1-3],
|
||||
[`overflowing_mul_add`][f-oma-1-3]
|
||||
* The new experimental feature [`unwrapped`][feat-un-1-3] was added,
|
||||
providing arithmetic methods that panic on overflow even when
|
||||
debug assertions are disabled.
|
||||
|
||||
[f-cma-1-3]: https://tspiteri.gitlab.io/fixed/dev/fixed/struct.FixedI32.html#method.checked_mul_add
|
||||
[f-ma-1-3]: https://tspiteri.gitlab.io/fixed/dev/fixed/struct.FixedI32.html#method.mul_add
|
||||
[f-oma-1-3]: https://tspiteri.gitlab.io/fixed/dev/fixed/struct.FixedI32.html#method.overflowing_mul_add
|
||||
[f-sma-1-3]: https://tspiteri.gitlab.io/fixed/dev/fixed/struct.FixedI32.html#method.saturating_mul_add
|
||||
[f-wma-1-3]: https://tspiteri.gitlab.io/fixed/dev/fixed/struct.FixedI32.html#method.wrapping_mul_add
|
||||
[feat-un-1-3]: https://tspiteri.gitlab.io/fixed/dev/fixed/#experimental-optional-features
|
||||
|
||||
Version 1.2.0 (2020-09-02)
|
||||
|
|
101
src/arith.rs
101
src/arith.rs
|
@ -457,6 +457,7 @@ fixed_arith! { FixedI128(i128, LeEqU128, 128), Signed }
|
|||
|
||||
pub(crate) trait MulDivOverflow: Sized {
|
||||
fn mul_overflow(self, rhs: Self, frac_nbits: u32) -> (Self, bool);
|
||||
fn mul_add_overflow(self, mul: Self, add: Self, frac_nbits: u32) -> (Self, bool);
|
||||
fn div_overflow(self, rhs: Self, frac_nbits: u32) -> (Self, bool);
|
||||
}
|
||||
|
||||
|
@ -473,6 +474,41 @@ macro_rules! mul_div_widen {
|
|||
((prod2 >> NBITS) as $Single, overflow)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn mul_add_overflow(
|
||||
self,
|
||||
mul: $Single,
|
||||
add: $Single,
|
||||
frac_nbits: u32,
|
||||
) -> ($Single, bool) {
|
||||
type Unsigned = <$Single as IntHelper>::Unsigned;
|
||||
const NBITS: u32 = <$Single>::NBITS;
|
||||
let self2 = <$Double>::from(self);
|
||||
let mul2 = <$Double>::from(mul);
|
||||
let prod2 = self2 * mul2;
|
||||
let lo = (prod2 >> frac_nbits) as Unsigned;
|
||||
let hi = (prod2 >> frac_nbits >> NBITS) as $Single;
|
||||
if_signed_unsigned! {
|
||||
$Signedness,
|
||||
{
|
||||
let (uns, carry) = lo.overflowing_add(add as Unsigned);
|
||||
let ans = uns as $Single;
|
||||
let mut expected_hi = if ans < 0 { -1 } else { 0 };
|
||||
if add < 0 {
|
||||
expected_hi += 1;
|
||||
}
|
||||
if carry {
|
||||
expected_hi -= 1;
|
||||
}
|
||||
(ans, hi != expected_hi)
|
||||
},
|
||||
{
|
||||
let (ans, overflow) = lo.overflowing_add(add);
|
||||
(ans, overflow || hi != 0)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn div_overflow(self, rhs: $Single, frac_nbits: u32) -> ($Single, bool) {
|
||||
const NBITS: u32 = <$Single>::NBITS;
|
||||
|
@ -497,6 +533,7 @@ trait FallbackHelper: Sized {
|
|||
fn shift_lo_up(self) -> Self;
|
||||
fn shift_lo_up_unsigned(self) -> Self::Unsigned;
|
||||
fn combine_lo_then_shl(self, lo: Self::Unsigned, shift: u32) -> (Self, bool);
|
||||
fn combine_lo_then_shl_add(self, lo: Self::Unsigned, shift: u32, add: Self) -> (Self, bool);
|
||||
fn carrying_add(self, other: Self) -> (Self, Self);
|
||||
}
|
||||
|
||||
|
@ -532,6 +569,21 @@ impl FallbackHelper for u128 {
|
|||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn combine_lo_then_shl_add(self, lo: u128, shift: u32, add: u128) -> (u128, bool) {
|
||||
let (combine, overflow1) = if shift == 128 {
|
||||
(self, false)
|
||||
} else if shift == 0 {
|
||||
(lo, self != 0)
|
||||
} else {
|
||||
let lo = lo >> shift;
|
||||
let hi = self << (128 - shift);
|
||||
(lo | hi, self >> shift != 0)
|
||||
};
|
||||
let (ans, overflow2) = combine.overflowing_add(add);
|
||||
(ans, overflow1 || overflow2)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn carrying_add(self, rhs: u128) -> (u128, u128) {
|
||||
let (sum, overflow) = self.overflowing_add(rhs);
|
||||
|
@ -574,6 +626,30 @@ impl FallbackHelper for i128 {
|
|||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn combine_lo_then_shl_add(self, lo: u128, shift: u32, add: i128) -> (i128, bool) {
|
||||
let (combine_lo, combine_hi) = if shift == 128 {
|
||||
(self as u128, if self < 0 { -1 } else { 0 })
|
||||
} else if shift == 0 {
|
||||
(lo, self)
|
||||
} else {
|
||||
(
|
||||
(lo >> shift) | (self << (128 - shift)) as u128,
|
||||
self >> shift,
|
||||
)
|
||||
};
|
||||
let (uns, carry) = combine_lo.overflowing_add(add as u128);
|
||||
let ans = uns as i128;
|
||||
let mut expected_hi = if ans < 0 { -1 } else { 0 };
|
||||
if add < 0 {
|
||||
expected_hi += 1;
|
||||
}
|
||||
if carry {
|
||||
expected_hi -= 1;
|
||||
}
|
||||
(ans, combine_hi != expected_hi)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn carrying_add(self, rhs: i128) -> (i128, i128) {
|
||||
let (sum, overflow) = self.overflowing_add(rhs);
|
||||
|
@ -616,6 +692,31 @@ macro_rules! mul_div_fallback {
|
|||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn mul_add_overflow(
|
||||
self,
|
||||
mul: $Single,
|
||||
add: $Single,
|
||||
frac_nbits: u32,
|
||||
) -> ($Single, bool) {
|
||||
// l * r + a
|
||||
let (lh, ll) = self.hi_lo();
|
||||
let (rh, rl) = mul.hi_lo();
|
||||
let ll_rl = ll.wrapping_mul(rl);
|
||||
let lh_rl = lh.wrapping_mul(rl);
|
||||
let ll_rh = ll.wrapping_mul(rh);
|
||||
let lh_rh = lh.wrapping_mul(rh);
|
||||
|
||||
let col01 = ll_rl as <$Single as FallbackHelper>::Unsigned;
|
||||
let (col01_hi, col01_lo) = col01.hi_lo();
|
||||
let partial_col12 = lh_rl + col01_hi as $Single;
|
||||
let (col12, carry_col3) = partial_col12.carrying_add(ll_rh);
|
||||
let (col12_hi, col12_lo) = col12.hi_lo();
|
||||
let ans01 = col12_lo.shift_lo_up_unsigned() + col01_lo;
|
||||
let ans23 = lh_rh + col12_hi + carry_col3.shift_lo_up();
|
||||
ans23.combine_lo_then_shl_add(ans01, frac_nbits, add)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn div_overflow(self, rhs: $Single, frac_nbits: u32) -> ($Single, bool) {
|
||||
if frac_nbits == 0 {
|
||||
|
|
|
@ -444,7 +444,7 @@ assert_eq!(two_point_75.to_string(), \"2.8\");
|
|||
|
||||
// inherent methods that do not require Frac bounds, some of which can thus be const
|
||||
fixed_no_frac! {
|
||||
$Fixed[$s_fixed]($Inner[$s_inner], $s_nbits),
|
||||
$Fixed[$s_fixed]($Inner[$s_inner], $LeEqU, $s_nbits),
|
||||
$nbytes, $bytes_val, $be_bytes, $le_bytes,
|
||||
$UInner, $Signedness
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
|
||||
macro_rules! fixed_no_frac {
|
||||
(
|
||||
$Fixed:ident[$s_fixed:expr]($Inner:ty[$s_inner:expr], $s_nbits:expr),
|
||||
$Fixed:ident[$s_fixed:expr]($Inner:ty[$s_inner:expr], $LeEqU:tt, $s_nbits:expr),
|
||||
$nbytes:expr, $bytes_val:expr, $be_bytes:expr, $le_bytes:expr,
|
||||
$UInner:ty, $Signedness:tt
|
||||
) => {
|
||||
|
@ -458,6 +458,66 @@ assert!(half.is_power_of_two());
|
|||
}
|
||||
}
|
||||
|
||||
comment! {
|
||||
"Multiply and add. Returns `self` × `mul` + `add`.
|
||||
|
||||
",
|
||||
if_signed_else_empty_str! {
|
||||
$Signedness,
|
||||
"The product `self` × `mul` does not need to be
|
||||
representable for a valid computation: if the product would overflow
|
||||
but the final result would not overflow, this method still returns the
|
||||
correct result.
|
||||
|
||||
",
|
||||
},
|
||||
"The `mul` parameter can have a fixed-point type like
|
||||
`self` but with a different number of fractional bits.
|
||||
|
||||
# Panics
|
||||
|
||||
When debug assertions are enabled, this method panics if the result
|
||||
overflows. When debug assertions are not enabled, the wrapped value
|
||||
can be returned, but it is not considered a breaking change if in the
|
||||
future it panics; if wrapping is required use [`wrapping_mul_add`]
|
||||
instead.
|
||||
|
||||
# Examples
|
||||
|
||||
```rust
|
||||
use fixed::{types::extra::U4, ", $s_fixed, "};
|
||||
type Fix = ", $s_fixed, "<U4>;
|
||||
assert_eq!(
|
||||
Fix::from_num(4).mul_add(Fix::from_num(0.5), Fix::from_num(3)),
|
||||
Fix::from_num(5)
|
||||
);
|
||||
",
|
||||
if_signed_else_empty_str! {
|
||||
$Signedness,
|
||||
"// MAX × 1.5 − MAX = MAX / 2, which does not overflow
|
||||
assert_eq!(Fix::MAX.mul_add(Fix::from_num(1.5), -Fix::MAX), Fix::MAX / 2);
|
||||
"
|
||||
},
|
||||
"```
|
||||
|
||||
[`wrapping_mul_add`]: #method.wrapping_mul_add
|
||||
";
|
||||
#[inline]
|
||||
pub fn mul_add<MulFrac: $LeEqU>(
|
||||
self,
|
||||
mul: $Fixed<MulFrac>,
|
||||
add: $Fixed<Frac>,
|
||||
) -> $Fixed<Frac> {
|
||||
let (ans, overflow) = self.to_bits().mul_add_overflow(
|
||||
mul.to_bits(),
|
||||
add.to_bits(),
|
||||
MulFrac::U32,
|
||||
);
|
||||
debug_assert!(!overflow, "overflow");
|
||||
Self::from_bits(ans)
|
||||
}
|
||||
}
|
||||
|
||||
comment! {
|
||||
"Remainder for Euclidean division.
|
||||
|
||||
|
@ -654,6 +714,62 @@ assert_eq!(Fix::from_num(1.5).checked_rem(Fix::from_num(0)), None);
|
|||
}
|
||||
}
|
||||
|
||||
comment! {
|
||||
"Checked multiply and add.
|
||||
Returns `self` × `mul` + `add`, or [`None`] on overflow.
|
||||
|
||||
",
|
||||
if_signed_else_empty_str! {
|
||||
$Signedness,
|
||||
"The product `self` × `mul` does not need to be
|
||||
representable for a valid computation: if the product would overflow
|
||||
but the final result would not overflow, this method still returns the
|
||||
correct result.
|
||||
|
||||
",
|
||||
},
|
||||
"The `mul` parameter can have a fixed-point type like
|
||||
`self` but with a different number of fractional bits.
|
||||
|
||||
# Examples
|
||||
|
||||
```rust
|
||||
use fixed::{types::extra::U4, ", $s_fixed, "};
|
||||
type Fix = ", $s_fixed, "<U4>;
|
||||
assert_eq!(
|
||||
Fix::from_num(4).checked_mul_add(Fix::from_num(0.5), Fix::from_num(3)),
|
||||
Some(Fix::from_num(5))
|
||||
);
|
||||
assert_eq!(Fix::MAX.checked_mul_add(Fix::from_num(1), Fix::from_num(0)), Some(Fix::MAX));
|
||||
assert_eq!(Fix::MAX.checked_mul_add(Fix::from_num(1), Fix::from_bits(1)), None);
|
||||
",
|
||||
if_signed_else_empty_str! {
|
||||
$Signedness,
|
||||
"// MAX × 1.5 − MAX = MAX / 2, which does not overflow
|
||||
assert_eq!(Fix::MAX.checked_mul_add(Fix::from_num(1.5), -Fix::MAX), Some(Fix::MAX / 2));
|
||||
"
|
||||
},
|
||||
"```
|
||||
|
||||
[`None`]: https://doc.rust-lang.org/nightly/core/option/enum.Option.html#variant.None
|
||||
";
|
||||
#[inline]
|
||||
pub fn checked_mul_add<MulFrac: $LeEqU>(
|
||||
self,
|
||||
mul: $Fixed<MulFrac>,
|
||||
add: $Fixed<Frac>,
|
||||
) -> Option<$Fixed<Frac>> {
|
||||
match self.to_bits().mul_add_overflow(
|
||||
mul.to_bits(),
|
||||
add.to_bits(),
|
||||
MulFrac::U32,
|
||||
) {
|
||||
(ans, false) => Some(Self::from_bits(ans)),
|
||||
(_, true) => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
comment! {
|
||||
"Checked multiplication by an integer. Returns the
|
||||
product, or [`None`] on overflow.
|
||||
|
@ -946,6 +1062,72 @@ assert_eq!(Fix::from_num(0).saturating_sub(Fix::from_num(1)), Fix::from_num(0));
|
|||
}
|
||||
}
|
||||
|
||||
comment! {
|
||||
"Saturating multiply and add.
|
||||
Returns `self` × `mul` + `add`, saturating on overflow.
|
||||
|
||||
",
|
||||
if_signed_else_empty_str! {
|
||||
$Signedness,
|
||||
"The product `self` × `mul` does not need to be
|
||||
representable for a valid computation: if the product would overflow
|
||||
but the final result would not overflow, this method still returns the
|
||||
correct result.
|
||||
|
||||
",
|
||||
},
|
||||
"The `mul` parameter can have a fixed-point type like
|
||||
`self` but with a different number of fractional bits.
|
||||
|
||||
# Examples
|
||||
|
||||
```rust
|
||||
use fixed::{types::extra::U4, ", $s_fixed, "};
|
||||
type Fix = ", $s_fixed, "<U4>;
|
||||
assert_eq!(
|
||||
Fix::from_num(4).saturating_mul_add(Fix::from_num(0.5), Fix::from_num(3)),
|
||||
Fix::from_num(5)
|
||||
);
|
||||
let half_max = Fix::MAX / 2;
|
||||
assert_eq!(half_max.saturating_mul_add(Fix::from_num(3), half_max), Fix::MAX);
|
||||
",
|
||||
if_signed_else_empty_str! {
|
||||
$Signedness,
|
||||
"assert_eq!(half_max.saturating_mul_add(Fix::from_num(-5), half_max), Fix::MIN);
|
||||
// MAX × 1.5 − MAX = MAX / 2, which does not overflow
|
||||
assert_eq!(Fix::MAX.saturating_mul_add(Fix::from_num(1.5), -Fix::MAX), half_max);
|
||||
"
|
||||
},
|
||||
"```
|
||||
";
|
||||
#[inline]
|
||||
pub fn saturating_mul_add<MulFrac: $LeEqU>(
|
||||
self,
|
||||
mul: $Fixed<MulFrac>,
|
||||
add: $Fixed<Frac>,
|
||||
) -> $Fixed<Frac> {
|
||||
match self.to_bits().mul_add_overflow(
|
||||
mul.to_bits(),
|
||||
add.to_bits(),
|
||||
MulFrac::U32,
|
||||
) {
|
||||
(ans, false) => Self::from_bits(ans),
|
||||
(_, true) => {
|
||||
let negative = if_signed_unsigned! {
|
||||
$Signedness,
|
||||
self.is_negative() != mul.is_negative(),
|
||||
false,
|
||||
};
|
||||
if negative {
|
||||
Self::MIN
|
||||
} else {
|
||||
Self::MAX
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
comment! {
|
||||
"Saturating multiplication by an integer. Returns the product, saturating on overflow.
|
||||
|
||||
|
@ -1083,6 +1265,43 @@ assert_eq!(Fix::from_num(0)",
|
|||
}
|
||||
}
|
||||
|
||||
comment! {
|
||||
"Wrapping multiply and add.
|
||||
Returns `self` × `mul` + `add`, wrapping on overflow.
|
||||
|
||||
The `mul` parameter can have a fixed-point type like
|
||||
`self` but with a different number of fractional bits.
|
||||
|
||||
# Examples
|
||||
|
||||
```rust
|
||||
use fixed::{types::extra::U4, ", $s_fixed, "};
|
||||
type Fix = ", $s_fixed, "<U4>;
|
||||
assert_eq!(
|
||||
Fix::from_num(4).wrapping_mul_add(Fix::from_num(0.5), Fix::from_num(3)),
|
||||
Fix::from_num(5)
|
||||
);
|
||||
assert_eq!(Fix::MAX.wrapping_mul_add(Fix::from_num(1), Fix::from_num(0)), Fix::MAX);
|
||||
assert_eq!(Fix::MAX.wrapping_mul_add(Fix::from_num(1), Fix::from_bits(1)), Fix::MIN);
|
||||
let wrapped = Fix::from_bits(!0 << 2);
|
||||
assert_eq!(Fix::MAX.wrapping_mul_add(Fix::from_num(3), Fix::MAX), wrapped);
|
||||
```
|
||||
";
|
||||
#[inline]
|
||||
pub fn wrapping_mul_add<MulFrac: $LeEqU>(
|
||||
self,
|
||||
mul: $Fixed<MulFrac>,
|
||||
add: $Fixed<Frac>,
|
||||
) -> $Fixed<Frac> {
|
||||
let (ans, _) = self.to_bits().mul_add_overflow(
|
||||
mul.to_bits(),
|
||||
add.to_bits(),
|
||||
MulFrac::U32,
|
||||
);
|
||||
Self::from_bits(ans)
|
||||
}
|
||||
}
|
||||
|
||||
comment! {
|
||||
"Wrapping multiplication by an integer. Returns the product, wrapping on overflow.
|
||||
|
||||
|
@ -1366,6 +1585,69 @@ let _overflow = Fix::MIN.unwrapped_sub(Fix::from_bits(1));
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "unwrapped")]
|
||||
comment! {
|
||||
"Unwrapped multiply and add.
|
||||
Returns `self` × `mul` + `add`, panicking on overflow.
|
||||
|
||||
",
|
||||
if_signed_else_empty_str! {
|
||||
$Signedness,
|
||||
"The product `self` × `mul` does not need to be
|
||||
representable for a valid computation: if the product would overflow
|
||||
but the final result would not overflow, this method still returns the
|
||||
correct result.
|
||||
|
||||
",
|
||||
},
|
||||
"The `mul` parameter can have a fixed-point type like
|
||||
`self` but with a different number of fractional bits.
|
||||
|
||||
This method is only available when the [`unwrapped` experimental
|
||||
feature][exp] is enabled.
|
||||
|
||||
# Panics
|
||||
|
||||
Panics if the result does not fit.
|
||||
|
||||
# Examples
|
||||
|
||||
```rust
|
||||
use fixed::{types::extra::U4, ", $s_fixed, "};
|
||||
type Fix = ", $s_fixed, "<U4>;
|
||||
assert_eq!(
|
||||
Fix::from_num(4).unwrapped_mul_add(Fix::from_num(0.5), Fix::from_num(3)),
|
||||
Fix::from_num(5)
|
||||
);
|
||||
",
|
||||
if_signed_else_empty_str! {
|
||||
$Signedness,
|
||||
"// MAX × 1.5 − MAX = MAX / 2, which does not overflow
|
||||
assert_eq!(Fix::MAX.unwrapped_mul_add(Fix::from_num(1.5), -Fix::MAX), Fix::MAX / 2);
|
||||
"
|
||||
},
|
||||
"```
|
||||
|
||||
The following panics because of overflow.
|
||||
|
||||
```should_panic
|
||||
use fixed::{types::extra::U4, ", $s_fixed, "};
|
||||
type Fix = ", $s_fixed, "<U4>;
|
||||
let _overflow = Fix::MAX.unwrapped_mul_add(Fix::from_num(1), Fix::from_bits(1));
|
||||
```
|
||||
|
||||
[exp]: index.html#experimental-optional-features
|
||||
";
|
||||
#[inline]
|
||||
pub fn unwrapped_mul_add<MulFrac: $LeEqU>(
|
||||
self,
|
||||
mul: $Fixed<MulFrac>,
|
||||
add: $Fixed<Frac>,
|
||||
) -> $Fixed<Frac> {
|
||||
self.checked_mul_add(mul, add).expect("overflow")
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "unwrapped")]
|
||||
comment! {
|
||||
"Unwrapped multiplication by an integer. Returns the product, panicking on overflow.
|
||||
|
@ -1718,6 +2000,53 @@ assert_eq!(Fix::from_num(0)",
|
|||
}
|
||||
}
|
||||
|
||||
comment! {
|
||||
"Overflowing multiply and add.
|
||||
|
||||
Returns a [tuple] of `self` × `mul` + `add` and a [`bool`] indicating
|
||||
whether an overflow has occurred. On overflow, the wrapped value is
|
||||
returned.
|
||||
|
||||
The `mul` parameter can have a fixed-point type like
|
||||
`self` but with a different number of fractional bits.
|
||||
|
||||
# Examples
|
||||
|
||||
```rust
|
||||
use fixed::{types::extra::U4, ", $s_fixed, "};
|
||||
type Fix = ", $s_fixed, "<U4>;
|
||||
assert_eq!(
|
||||
Fix::MAX.overflowing_mul_add(Fix::from_num(1), Fix::from_num(0)),
|
||||
(Fix::MAX, false)
|
||||
);
|
||||
assert_eq!(
|
||||
Fix::MAX.overflowing_mul_add(Fix::from_num(1), Fix::from_bits(1)),
|
||||
(Fix::MIN, true)
|
||||
);
|
||||
assert_eq!(
|
||||
Fix::MAX.overflowing_mul_add(Fix::from_num(3), Fix::MAX),
|
||||
(Fix::from_bits(!0 << 2), 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_mul_add<MulFrac: $LeEqU>(
|
||||
self,
|
||||
mul: $Fixed<MulFrac>,
|
||||
add: $Fixed<Frac>,
|
||||
) -> ($Fixed<Frac>, bool) {
|
||||
let (ans, overflow) = self.to_bits().mul_add_overflow(
|
||||
mul.to_bits(),
|
||||
add.to_bits(),
|
||||
MulFrac::U32,
|
||||
);
|
||||
(Self::from_bits(ans), overflow)
|
||||
}
|
||||
}
|
||||
|
||||
comment! {
|
||||
"Overflowing multiplication by an integer.
|
||||
|
||||
|
|
|
@ -838,6 +838,9 @@ where
|
|||
/// Shifts to the right by `n` bits, wrapping the truncated bits to the left end.
|
||||
fn rotate_right(self, n: u32) -> Self;
|
||||
|
||||
/// Multiply and add. Returns `self` × `mul` + `add`.
|
||||
fn mul_add(self, mul: Self, add: Self) -> Self;
|
||||
|
||||
/// Euclidean division by an integer.
|
||||
///
|
||||
/// # Panics
|
||||
|
@ -898,6 +901,11 @@ where
|
|||
/// [`None`]: https://doc.rust-lang.org/nightly/core/option/enum.Option.html#variant.None
|
||||
fn checked_rem(self, rhs: Self) -> Option<Self>;
|
||||
|
||||
/// Checked multiply and add. Returns `self` × `mul` + `add`, or [`None`] on overflow.
|
||||
///
|
||||
/// [`None`]: https://doc.rust-lang.org/nightly/core/option/enum.Option.html#variant.None
|
||||
fn checked_mul_add(self, mul: Self, add: Self) -> Option<Self>;
|
||||
|
||||
/// Checked remainder for Euclidean division. Returns the
|
||||
/// remainder, or [`None`] if the divisor is zero or the division
|
||||
/// results in overflow.
|
||||
|
@ -976,6 +984,9 @@ where
|
|||
/// Panics if the divisor is zero.
|
||||
fn saturating_div(self, rhs: Self) -> Self;
|
||||
|
||||
/// Saturating multiply and add. Returns `self` × `mul` + `add`, saturating on overflow.
|
||||
fn saturating_mul_add(self, mul: Self, add: Self) -> Self;
|
||||
|
||||
/// Saturating Euclidean division. Returns the quotient, saturating on overflow.
|
||||
///
|
||||
/// # Panics
|
||||
|
@ -1005,6 +1016,9 @@ where
|
|||
/// Panics if the divisor is zero.
|
||||
fn wrapping_div(self, rhs: Self) -> Self;
|
||||
|
||||
/// Wrapping multiply and add. Returns `self` × `mul` + `add`, wrapping on overflow.
|
||||
fn wrapping_mul_add(self, mul: Self, add: Self) -> Self;
|
||||
|
||||
/// Wrapping Euclidean division. Returns the quotient, wrapping on overflow.
|
||||
///
|
||||
/// # Panics
|
||||
|
@ -1115,6 +1129,19 @@ where
|
|||
/// [exp]: ../index.html#experimental-optional-features
|
||||
fn unwrapped_div(self, rhs: Self) -> Self;
|
||||
|
||||
#[cfg(feature = "unwrapped")]
|
||||
/// Unwrapped multiply and add. Returns `self` × `mul` + `add`, panicking on overflow.
|
||||
///
|
||||
/// This method is only available when the [`unwrapped`
|
||||
/// experimental feature][exp] is enabled.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the result does not fit.
|
||||
///
|
||||
/// [exp]: ../index.html#experimental-optional-features
|
||||
fn unwrapped_mul_add(self, mul: Self, add: Self) -> Self;
|
||||
|
||||
#[cfg(feature = "unwrapped")]
|
||||
/// Unwrapped Euclidean division. Returns the quotient, panicking on overflow.
|
||||
///
|
||||
|
@ -1266,6 +1293,16 @@ where
|
|||
/// [tuple]: https://doc.rust-lang.org/nightly/std/primitive.tuple.html
|
||||
fn overflowing_div(self, rhs: Self) -> (Self, bool);
|
||||
|
||||
/// Overflowing multiply and add.
|
||||
///
|
||||
/// Returns a [tuple] of `self` × `mul` + `add` and a [`bool`],
|
||||
/// indicating whether an overflow has occurred. On overflow, the
|
||||
/// wrapped value is returned.
|
||||
///
|
||||
/// [`bool`]: https://doc.rust-lang.org/nightly/std/primitive.bool.html
|
||||
/// [tuple]: https://doc.rust-lang.org/nightly/std/primitive.tuple.html
|
||||
fn overflowing_mul_add(self, mul: Self, add: Self) -> (Self, bool);
|
||||
|
||||
/// Overflowing Euclidean division.
|
||||
///
|
||||
/// Returns a [tuple] of the quotient and a [`bool`], indicating
|
||||
|
@ -2361,6 +2398,7 @@ macro_rules! impl_fixed {
|
|||
trait_delegate! { fn checked_int_log10(self) -> Option<i32> }
|
||||
trait_delegate! { fn rotate_left(self, n: u32) -> Self }
|
||||
trait_delegate! { fn rotate_right(self, n: u32) -> Self }
|
||||
trait_delegate! { fn mul_add(self, mul: Self, add: Self) -> Self }
|
||||
trait_delegate! { fn div_euclid(self, rhs: Self) -> Self }
|
||||
trait_delegate! { fn rem_euclid(self, rhs: Self) -> Self }
|
||||
trait_delegate! { fn div_euclid_int(self, rhs: Self::Bits) -> Self }
|
||||
|
@ -2371,6 +2409,7 @@ macro_rules! impl_fixed {
|
|||
trait_delegate! { fn checked_mul(self, rhs: Self) -> Option<Self> }
|
||||
trait_delegate! { fn checked_div(self, rhs: Self) -> Option<Self> }
|
||||
trait_delegate! { fn checked_rem(self, rhs: Self) -> Option<Self> }
|
||||
trait_delegate! { fn checked_mul_add(self, mul: Self, add: Self) -> Option<Self> }
|
||||
trait_delegate! { fn checked_div_euclid(self, rhs: Self) -> Option<Self> }
|
||||
trait_delegate! { fn checked_rem_euclid(self, rhs: Self) -> Option<Self> }
|
||||
trait_delegate! { fn checked_mul_int(self, rhs: Self::Bits) -> Option<Self> }
|
||||
|
@ -2385,6 +2424,7 @@ macro_rules! impl_fixed {
|
|||
trait_delegate! { fn saturating_sub(self, rhs: Self) -> Self }
|
||||
trait_delegate! { fn saturating_mul(self, rhs: Self) -> Self }
|
||||
trait_delegate! { fn saturating_div(self, rhs: Self) -> Self }
|
||||
trait_delegate! { fn saturating_mul_add(self, mul: Self, add: Self) -> Self }
|
||||
trait_delegate! { fn saturating_div_euclid(self, rhs: Self) -> Self }
|
||||
trait_delegate! { fn saturating_mul_int(self, rhs: Self::Bits) -> Self }
|
||||
trait_delegate! { fn wrapping_neg(self) -> Self }
|
||||
|
@ -2392,6 +2432,7 @@ macro_rules! impl_fixed {
|
|||
trait_delegate! { fn wrapping_sub(self, rhs: Self) -> Self }
|
||||
trait_delegate! { fn wrapping_mul(self, rhs: Self) -> Self }
|
||||
trait_delegate! { fn wrapping_div(self, rhs: Self) -> Self }
|
||||
trait_delegate! { fn wrapping_mul_add(self, mul: Self, add: Self) -> Self }
|
||||
trait_delegate! { fn wrapping_div_euclid(self, rhs: Self) -> Self }
|
||||
trait_delegate! { fn wrapping_mul_int(self, rhs: Self::Bits) -> Self }
|
||||
trait_delegate! { fn wrapping_div_int(self, rhs: Self::Bits) -> Self }
|
||||
|
@ -2410,6 +2451,8 @@ macro_rules! impl_fixed {
|
|||
#[cfg(feature = "unwrapped")]
|
||||
trait_delegate! { fn unwrapped_div(self, rhs: Self) -> Self }
|
||||
#[cfg(feature = "unwrapped")]
|
||||
trait_delegate! { fn unwrapped_mul_add(self, mul: Self, add: Self) -> Self }
|
||||
#[cfg(feature = "unwrapped")]
|
||||
trait_delegate! { fn unwrapped_div_euclid(self, rhs: Self) -> Self }
|
||||
#[cfg(feature = "unwrapped")]
|
||||
trait_delegate! { fn unwrapped_mul_int(self, rhs: Self::Bits) -> Self }
|
||||
|
@ -2428,6 +2471,7 @@ macro_rules! impl_fixed {
|
|||
trait_delegate! { fn overflowing_sub(self, rhs: Self) -> (Self, bool) }
|
||||
trait_delegate! { fn overflowing_mul(self, rhs: Self) -> (Self, bool) }
|
||||
trait_delegate! { fn overflowing_div(self, rhs: Self) -> (Self, bool) }
|
||||
trait_delegate! { fn overflowing_mul_add(self, mul: Self, add: Self) -> (Self, bool) }
|
||||
trait_delegate! { fn overflowing_div_euclid(self, rhs: Self) -> (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) }
|
||||
|
|
Loading…
Reference in New Issue