implement reciprocal methods
This commit is contained in:
parent
20e5243c45
commit
b09acfdf56
86
src/lib.rs
86
src/lib.rs
|
@ -382,7 +382,7 @@ macro_rules! fixed {
|
|||
$s_nbits_m1:expr, $s_nbits_m2:expr, $s_nbits_m3:expr, $s_nbits_m4:expr
|
||||
),
|
||||
$nbytes:expr, $bytes_val:expr, $be_bytes:expr, $le_bytes:expr,
|
||||
$UInner:ty, $Signedness:tt,
|
||||
$UFixed:ident, $UInner:ty, $Signedness:tt,
|
||||
$LeEqU_C0:tt, $LeEqU_C1:tt, $LeEqU_C2:tt, $LeEqU_C3:tt
|
||||
) => {
|
||||
fixed! {
|
||||
|
@ -392,7 +392,7 @@ macro_rules! fixed {
|
|||
$s_nbits_m1, $s_nbits_m2, $s_nbits_m3, $s_nbits_m4
|
||||
),
|
||||
$nbytes, $bytes_val, $be_bytes, $le_bytes,
|
||||
$UInner, $Signedness,
|
||||
$UFixed, $UInner, $Signedness,
|
||||
$LeEqU_C0, $LeEqU_C1, $LeEqU_C2, $LeEqU_C3
|
||||
}
|
||||
};
|
||||
|
@ -403,7 +403,7 @@ macro_rules! fixed {
|
|||
$s_nbits_m1:expr, $s_nbits_m2:expr, $s_nbits_m3:expr, $s_nbits_m4:expr
|
||||
),
|
||||
$nbytes:expr, $bytes_val:expr, $be_bytes:expr, $le_bytes:expr,
|
||||
$UInner:ty, $Signedness:tt,
|
||||
$UFixed:ident, $UInner:ty, $Signedness:tt,
|
||||
$LeEqU_C0:tt, $LeEqU_C1:tt, $LeEqU_C2:tt, $LeEqU_C3:tt
|
||||
) => {
|
||||
comment! {
|
||||
|
@ -477,7 +477,7 @@ assert_eq!(two_point_75.to_string(), \"2.8\");
|
|||
// inherent methods that require Frac bounds, and cannot be const
|
||||
fixed_frac! {
|
||||
$Fixed[$s_fixed]($Inner[$s_inner], $LeEqU, $s_nbits, $s_nbits_m1, $s_nbits_m4),
|
||||
$UInner, $Signedness
|
||||
$UFixed, $UInner, $Signedness
|
||||
}
|
||||
fixed_const! {
|
||||
$Fixed[$s_fixed]($LeEqU, $s_nbits, $s_nbits_m1, $s_nbits_m2, $s_nbits_m3, $s_nbits_m4),
|
||||
|
@ -491,21 +491,21 @@ fixed! {
|
|||
"An eight-bit fixed-point unsigned",
|
||||
FixedU8(u8, LeEqU8, "8", "7", "6", "5", "4"),
|
||||
1, "0x12", "[0x12]", "[0x12]",
|
||||
u8, Unsigned,
|
||||
FixedU8, u8, Unsigned,
|
||||
U8, U7, U6, U5
|
||||
}
|
||||
fixed! {
|
||||
"A 16-bit fixed-point unsigned",
|
||||
FixedU16(u16, LeEqU16, "16", "15", "14", "13", "12"),
|
||||
2, "0x1234", "[0x12, 0x34]", "[0x34, 0x12]",
|
||||
u16, Unsigned,
|
||||
FixedU16, u16, Unsigned,
|
||||
U16, U15, U14, U13
|
||||
}
|
||||
fixed! {
|
||||
"A 32-bit fixed-point unsigned",
|
||||
FixedU32(u32, LeEqU32, "32", "31", "30", "29", "28"),
|
||||
4, "0x1234_5678", "[0x12, 0x34, 0x56, 0x78]", "[0x78, 0x56, 0x34, 0x12]",
|
||||
u32, Unsigned,
|
||||
FixedU32, u32, Unsigned,
|
||||
U32, U31, U30, U29
|
||||
}
|
||||
fixed! {
|
||||
|
@ -514,7 +514,7 @@ fixed! {
|
|||
8, "0x1234_5678_9ABC_DEF0",
|
||||
"[0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0]",
|
||||
"[0xF0, 0xDE, 0xBC, 0x9A, 0x78, 0x56, 0x34, 0x12]",
|
||||
u64, Unsigned,
|
||||
FixedU64, u64, Unsigned,
|
||||
U64, U63, U62, U61
|
||||
}
|
||||
fixed! {
|
||||
|
@ -525,28 +525,28 @@ fixed! {
|
|||
0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0]",
|
||||
"[0xF0, 0xDE, 0xBC, 0x9A, 0x78, 0x56, 0x34, 0x12, \
|
||||
0xF0, 0xDE, 0xBC, 0x9A, 0x78, 0x56, 0x34, 0x12]",
|
||||
u128, Unsigned,
|
||||
FixedU128, u128, Unsigned,
|
||||
U128, U127, U126, U125
|
||||
}
|
||||
fixed! {
|
||||
"An eight-bit fixed-point signed",
|
||||
FixedI8(i8, LeEqU8, "8", "7", "6", "5", "4"),
|
||||
1, "0x12", "[0x12]", "[0x12]",
|
||||
u8, Signed,
|
||||
FixedU8, u8, Signed,
|
||||
U7, U6, U5, U4
|
||||
}
|
||||
fixed! {
|
||||
"A 16-bit fixed-point signed",
|
||||
FixedI16(i16, LeEqU16, "16", "15", "14", "13", "12"),
|
||||
2, "0x1234", "[0x12, 0x34]", "[0x34, 0x12]",
|
||||
u16, Signed,
|
||||
FixedU16, u16, Signed,
|
||||
U15, U14, U13, U12
|
||||
}
|
||||
fixed! {
|
||||
"A 32-bit fixed-point signed",
|
||||
FixedI32(i32, LeEqU32, "32", "31", "30", "29", "28"),
|
||||
4, "0x1234_5678", "[0x12, 0x34, 0x56, 0x78]", "[0x78, 0x56, 0x34, 0x12]",
|
||||
u32, Signed,
|
||||
FixedU32, u32, Signed,
|
||||
U31, U30, U29, U28
|
||||
}
|
||||
fixed! {
|
||||
|
@ -555,7 +555,7 @@ fixed! {
|
|||
8, "0x1234_5678_9ABC_DEF0",
|
||||
"[0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0]",
|
||||
"[0xF0, 0xDE, 0xBC, 0x9A, 0x78, 0x56, 0x34, 0x12]",
|
||||
u64, Signed,
|
||||
FixedU64, u64, Signed,
|
||||
U63, U62, U61, U60
|
||||
}
|
||||
fixed! {
|
||||
|
@ -566,7 +566,7 @@ fixed! {
|
|||
0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0]",
|
||||
"[0xF0, 0xDE, 0xBC, 0x9A, 0x78, 0x56, 0x34, 0x12, \
|
||||
0xF0, 0xDE, 0xBC, 0x9A, 0x78, 0x56, 0x34, 0x12]",
|
||||
u128, Signed,
|
||||
FixedU128, u128, Signed,
|
||||
U127, U126, U125, U124
|
||||
}
|
||||
|
||||
|
@ -1316,4 +1316,62 @@ mod tests {
|
|||
(U16F16::from_num(4), false)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn reciprocals() {
|
||||
// 4/3 wraps to 1/3 = 0x0.5555_5555
|
||||
assert_eq!(
|
||||
U0F32::from_num(0.75).overflowing_recip(),
|
||||
(U0F32::from_bits(0x5555_5555), true)
|
||||
);
|
||||
// 8/3 wraps to 2/3 = 0x0.AAAA_AAAA
|
||||
assert_eq!(
|
||||
U0F32::from_num(0.375).overflowing_recip(),
|
||||
(U0F32::from_bits(0xAAAA_AAAA), true)
|
||||
);
|
||||
|
||||
// 8/3 wraps to 2/3 = 0x0.AAAA_AAAA, which is -0x0.5555_5556
|
||||
assert_eq!(
|
||||
I0F32::from_num(0.375).overflowing_recip(),
|
||||
(I0F32::from_bits(-0x5555_5556), true)
|
||||
);
|
||||
assert_eq!(
|
||||
I0F32::from_num(-0.375).overflowing_recip(),
|
||||
(I0F32::from_bits(0x5555_5556), true)
|
||||
);
|
||||
// -2 wraps to 0
|
||||
assert_eq!(
|
||||
I0F32::from_num(-0.5).overflowing_recip(),
|
||||
(I0F32::from_num(0), true)
|
||||
);
|
||||
|
||||
// 8/3 wraps to 2/3 = 0x0.AAAA_AAAA (bits 0x5555_5555)
|
||||
assert_eq!(
|
||||
I1F31::from_num(0.375).overflowing_recip(),
|
||||
(I1F31::from_bits(0x5555_5555), true)
|
||||
);
|
||||
assert_eq!(
|
||||
I1F31::from_num(-0.375).overflowing_recip(),
|
||||
(I1F31::from_bits(-0x5555_5555), true)
|
||||
);
|
||||
// 4/3 = 0x1.5555_5554 (bits 0xAAAA_AAAA, or -0x5555_5556)
|
||||
assert_eq!(
|
||||
I1F31::from_num(0.75).overflowing_recip(),
|
||||
(I1F31::from_bits(-0x5555_5556), true)
|
||||
);
|
||||
assert_eq!(
|
||||
I1F31::from_num(-0.75).overflowing_recip(),
|
||||
(I1F31::from_bits(0x5555_5556), true)
|
||||
);
|
||||
// -2 wraps to 0
|
||||
assert_eq!(
|
||||
I1F31::from_num(-0.5).overflowing_recip(),
|
||||
(I1F31::from_num(0), true)
|
||||
);
|
||||
// -1 does not overflow
|
||||
assert_eq!(
|
||||
I1F31::from_num(-1).overflowing_recip(),
|
||||
(I1F31::from_num(-1), false)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ macro_rules! fixed_frac {
|
|||
$Fixed:ident[$s_fixed:expr](
|
||||
$Inner:ty[$s_inner:expr], $LeEqU:tt, $s_nbits:expr, $s_nbits_m1:expr, $s_nbits_m4:expr
|
||||
),
|
||||
$UInner:ty, $Signedness:tt
|
||||
$UFixed:ident, $UInner:ty, $Signedness:tt
|
||||
) => {
|
||||
/// The implementation of items in this block depends on the
|
||||
/// number of fractional bits `Frac`.
|
||||
|
@ -223,6 +223,35 @@ assert_eq!(Fix::from_num(-5).signum(), -1);
|
|||
}
|
||||
}
|
||||
|
||||
comment! {
|
||||
"Returns the reciprocal (inverse) of the fixed-point number, 1/`self`.
|
||||
|
||||
# Panics
|
||||
|
||||
Panics if the fixed-point number is zero.
|
||||
|
||||
When debug assertions are enabled, this method also panics if the
|
||||
reciprocal 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_recip`] instead.
|
||||
|
||||
# Examples
|
||||
|
||||
```rust
|
||||
use fixed::{types::extra::U4, ", $s_fixed, "};
|
||||
type Fix = ", $s_fixed, "<U4>;
|
||||
assert_eq!(Fix::from_num(2).recip(), Fix::from_num(0.5));
|
||||
```
|
||||
|
||||
[`wrapping_recip`]: #method.wrapping_recip
|
||||
";
|
||||
#[inline] pub fn recip(self) -> $Fixed<Frac> {
|
||||
let (ans, overflow) = self.overflowing_recip();
|
||||
debug_assert!(!overflow, "overflow"); ans
|
||||
}
|
||||
}
|
||||
|
||||
comment! {
|
||||
"Euclidean division.
|
||||
|
||||
|
@ -443,6 +472,34 @@ assert_eq!(Fix::MAX.checked_div(Fix::from_num(1) / 2), None);
|
|||
}
|
||||
}
|
||||
|
||||
comment! {
|
||||
"Checked reciprocal. Returns the reciprocal, or
|
||||
[`None`] if `self` is zero or on overflow.
|
||||
|
||||
# Examples
|
||||
|
||||
```rust
|
||||
use fixed::{types::extra::U4, ", $s_fixed, "};
|
||||
type Fix = ", $s_fixed, "<U4>;
|
||||
assert_eq!(Fix::from_num(2).checked_recip(), Some(Fix::from_num(0.5)));
|
||||
assert_eq!(Fix::from_num(0).checked_recip(), None);
|
||||
```
|
||||
|
||||
[`None`]: https://doc.rust-lang.org/nightly/core/option/enum.Option.html#variant.None
|
||||
";
|
||||
#[inline]
|
||||
pub fn checked_recip(self) -> Option<$Fixed<Frac>> {
|
||||
if self.to_bits() == 0 {
|
||||
None
|
||||
} else {
|
||||
match self.overflowing_recip() {
|
||||
(ans, false) => Some(ans),
|
||||
(_, true) => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
comment! {
|
||||
"Checked Euclidean division. Returns the quotient, or
|
||||
[`None`] if the divisor is zero or on overflow.
|
||||
|
@ -730,6 +787,44 @@ assert_eq!(Fix::MAX.saturating_div(one_half), Fix::MAX);
|
|||
}
|
||||
}
|
||||
|
||||
comment! {
|
||||
"Saturating reciprocal. Returns the reciprocal,
|
||||
saturating on overflow.
|
||||
|
||||
# Panics
|
||||
|
||||
Panics if the fixed-point number is zero.
|
||||
|
||||
# Examples
|
||||
|
||||
```rust
|
||||
use fixed::{types::extra::U", $s_nbits_m1, ", ", $s_fixed, "};
|
||||
// only one integer bit
|
||||
type Fix = ", $s_fixed, "<U", $s_nbits_m1, ">;
|
||||
assert_eq!(Fix::from_num(0.25).saturating_recip(), Fix::MAX);
|
||||
",
|
||||
if_signed_else_empty_str! {
|
||||
$Signedness,
|
||||
"assert_eq!(Fix::from_num(-0.25).saturating_recip(), Fix::MIN);
|
||||
",
|
||||
},
|
||||
"```
|
||||
";
|
||||
#[inline]
|
||||
pub fn saturating_recip(self) -> $Fixed<Frac> {
|
||||
match self.overflowing_recip() {
|
||||
(ans, false) => ans,
|
||||
(_, true) => {
|
||||
if self < 0 {
|
||||
Self::MIN
|
||||
} else {
|
||||
Self::MAX
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
comment! {
|
||||
"Saturating Euclidean division. Returns the quotient,
|
||||
saturating on overflow.
|
||||
|
@ -859,6 +954,30 @@ assert_eq!(Fix::MAX.wrapping_div(quarter), wrapped);
|
|||
}
|
||||
}
|
||||
|
||||
comment! {
|
||||
"Wrapping reciprocal. Returns the reciprocal,
|
||||
wrapping on overflow.
|
||||
|
||||
# Panics
|
||||
|
||||
Panics if the fixed-point number is zero.
|
||||
|
||||
# Examples
|
||||
|
||||
```rust
|
||||
use fixed::{types::extra::U", $s_nbits_m1, ", ", $s_fixed, "};
|
||||
// only one integer bit
|
||||
type Fix = ", $s_fixed, "<U", $s_nbits_m1, ">;
|
||||
assert_eq!(Fix::from_num(0.25).wrapping_recip(), Fix::from_num(0));
|
||||
```
|
||||
";
|
||||
#[inline]
|
||||
pub fn wrapping_recip(self) -> $Fixed<Frac> {
|
||||
let (ans, _) = self.overflowing_recip();
|
||||
ans
|
||||
}
|
||||
}
|
||||
|
||||
comment! {
|
||||
"Wrapping Euclidean division. Returns the quotient, wrapping on overflow.
|
||||
|
||||
|
@ -1083,6 +1202,32 @@ let _overflow = Fix::MAX.unwrapped_div(quarter);
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "unwrapped")]
|
||||
comment! {
|
||||
"Unwrapped reciprocal. Returns the reciprocal,
|
||||
panicking on overflow.
|
||||
|
||||
# Panics
|
||||
|
||||
Panics if the fixed-point number is zero or on overflow.
|
||||
|
||||
# Examples
|
||||
|
||||
```rust
|
||||
use fixed::{types::extra::U4, ", $s_fixed, "};
|
||||
type Fix = ", $s_fixed, "<U4>;
|
||||
assert_eq!(Fix::from_num(0.25).unwrapped_recip(), Fix::from_num(4));
|
||||
```
|
||||
";
|
||||
#[inline]
|
||||
pub fn unwrapped_recip(self) -> $Fixed<Frac> {
|
||||
match self.overflowing_recip() {
|
||||
(_, true) => panic!("overflow"),
|
||||
(ans, false) => ans,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "unwrapped")]
|
||||
comment! {
|
||||
"Unwrapped Euclidean division. Returns the quotient, panicking on overflow.
|
||||
|
@ -1343,6 +1488,68 @@ assert_eq!(Fix::MAX.overflowing_div(quarter), (wrapped, true));
|
|||
}
|
||||
}
|
||||
|
||||
comment! {
|
||||
"Overflowing reciprocal.
|
||||
|
||||
Returns a [tuple] of the reciprocal and a [`bool`] indicating whether
|
||||
an overflow has occurred. On overflow, the wrapped value is returned.
|
||||
|
||||
# Panics
|
||||
|
||||
Panics if the fixed-point number is zero.
|
||||
|
||||
# Examples
|
||||
|
||||
```rust
|
||||
use fixed::{
|
||||
types::extra::{U4, U", $s_nbits_m1, "},
|
||||
", $s_fixed, ",
|
||||
};
|
||||
type Fix = ", $s_fixed, "<U4>;
|
||||
// only one integer bit
|
||||
type Small = ", $s_fixed, "<U", $s_nbits_m1, ">;
|
||||
assert_eq!(Fix::from_num(0.25).overflowing_recip(), (Fix::from_num(4), false));
|
||||
assert_eq!(Small::from_num(0.25).overflowing_recip(), (Small::from_num(0), 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_recip(self) -> ($Fixed<Frac>, bool) {
|
||||
if let Some(one) = Self::checked_from_num(1) {
|
||||
return one.overflowing_div(self);
|
||||
}
|
||||
if_signed! {
|
||||
$Signedness;
|
||||
use crate::int_helper::IntHelper;
|
||||
let (neg, abs) = self.to_bits().neg_abs();
|
||||
let uns_abs = $UFixed::<Frac>::from_bits(abs);
|
||||
let (uns_wrapped, overflow1) = uns_abs.overflowing_recip();
|
||||
let (wrapped, overflow2) =
|
||||
$Fixed::<Frac>::overflowing_from_num(uns_wrapped);
|
||||
if wrapped == $Fixed::<Frac>::MIN && neg {
|
||||
return (wrapped, overflow1);
|
||||
}
|
||||
if neg {
|
||||
// if we do not have overflow yet, we will not overflow now
|
||||
(wrapped.wrapping_neg(), overflow1 | overflow2)
|
||||
} else {
|
||||
(wrapped, overflow1 | overflow2)
|
||||
}
|
||||
}
|
||||
if_unsigned! {
|
||||
$Signedness;
|
||||
// 0 < x < 1: 1/x = 1 + (1 - x) / x, wrapped to (1 - x) / x
|
||||
// x.wrapping_neg() = 1 - x
|
||||
|
||||
// x = 0: we still get division by zero
|
||||
|
||||
(self.wrapping_neg().wrapping_div(self), true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
comment! {
|
||||
"Overflowing Euclidean division.
|
||||
|
||||
|
|
Loading…
Reference in New Issue