add to_fixed plus checked versions to traits Int and Float
This commit is contained in:
parent
c9e9686ac4
commit
f7300c7f8a
16
README.md
16
README.md
|
@ -44,13 +44,21 @@ numeric primitives are implemented. That is, you can use [`From`] or
|
|||
* The return type of [`to_int`] is now generic.
|
||||
* The new methods [`to_fixed`], [`to_float`], [`checked_to_fixed`],
|
||||
[`checked_to_int`] and [`checked_to_float`] were added.
|
||||
* The method [`to_fixed`][`Int::to_fixed`] and its checked versions
|
||||
were added to the [`Int`] trait.
|
||||
* The method [`to_fixed`][`Float::to_fixed`] and its checked
|
||||
versions were added to the [`Float`] trait.
|
||||
|
||||
[`checked_to_int`]: https://docs.rs/fixed/0.3.0/fixed/struct.FixedI32.html#method.checked_to_int
|
||||
[`checked_to_float`]: https://docs.rs/fixed/0.3.0/fixed/struct.FixedI32.html#method.checked_to_float
|
||||
[`Float::to_fixed`]: https://docs.rs/fixed/0.3.0/fixed/sealed/trait.Float.html#method.to_fixed
|
||||
[`Float`]: https://docs.rs/fixed/0.3.0/fixed/sealed/trait.Float.html
|
||||
[`Int::to_fixed`]: https://docs.rs/fixed/0.3.0/fixed/sealed/trait.Int.html#method.to_fixed
|
||||
[`Int`]: https://docs.rs/fixed/0.3.0/fixed/sealed/trait.Int.html
|
||||
[`checked_to_fixed`]: https://docs.rs/fixed/0.3.0/fixed/struct.FixedI32.html#method.checked_to_fixed
|
||||
[`checked_to_int`]: https://docs.rs/fixed/0.3.0/fixed/struct.FixedI32.html#method.checked_to_int
|
||||
[`checked_to_float`]: https://docs.rs/fixed/0.3.0/fixed/struct.FixedI32.html#method.checked_to_float
|
||||
[`checked_to_fixed`]: https://docs.rs/fixed/0.3.0/fixed/struct.FixedI32.html#method.checked_to_fixed
|
||||
[`checked_to_int`]: https://docs.rs/fixed/0.3.0/fixed/struct.FixedI32.html#method.checked_to_int
|
||||
[`to_fixed`]: https://docs.rs/fixed/0.3.0/fixed/struct.FixedI32.html#method.to_fixed
|
||||
[`to_float`]: https://docs.rs/fixed/0.3.0/fixed/struct.FixedI32.html#method.to_float
|
||||
[`to_int`]: https://docs.rs/fixed/0.3.0/fixed/struct.FixedI32.html#method.to_int
|
||||
|
||||
### Version 0.2.1 news (2019-01-29)
|
||||
|
||||
|
|
|
@ -11,6 +11,10 @@ Version 0.3.0 (unreleased)
|
|||
* The return type of `to_int` is now generic.
|
||||
* The new methods `to_fixed`, `to_float`, `checked_to_fixed`,
|
||||
`checked_to_int` and `checked_to_float` were added.
|
||||
* The method `to_fixed` and its checked versions were added to the
|
||||
`Int` trait.
|
||||
* The method `to_fixed` and its checked versions were added to the
|
||||
`Float` trait.
|
||||
|
||||
Version 0.2.1 (2019-01-29)
|
||||
==========================
|
||||
|
|
|
@ -1629,9 +1629,7 @@ assert_eq!(Fix::saturating_from_float(f64::NEG_INFINITY), Fix::min_value());
|
|||
where
|
||||
F: Float,
|
||||
{
|
||||
if val.is_nan() {
|
||||
panic!("NaN");
|
||||
}
|
||||
assert!(!val.is_nan(), "NaN");
|
||||
let saturated = if val.is_sign_negative() {
|
||||
Self::min_value()
|
||||
} else {
|
||||
|
|
291
src/sealed.rs
291
src/sealed.rs
|
@ -46,7 +46,130 @@ use {
|
|||
/// [`u32`]: https://doc.rust-lang.org/nightly/std/primitive.u32.html
|
||||
/// [`u64`]: https://doc.rust-lang.org/nightly/std/primitive.u64.html
|
||||
/// [`u8`]: https://doc.rust-lang.org/nightly/std/primitive.u8.html
|
||||
pub trait Int: SealedInt {}
|
||||
pub trait Int: SealedInt {
|
||||
/// Converts to a fixed-point number.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// In debug mode, panics if the value does not fit. In release
|
||||
/// mode the value is wrapped, but it is not considered a breaking
|
||||
/// change if in the future it panics; if wrapping is required use
|
||||
/// [`wrapping_to_fixed`] instead.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// use fixed::sealed::Int;
|
||||
/// type Fix = fixed::FixedI16<fixed::frac::U8>;
|
||||
/// let fix: Fix = 3.to_fixed();
|
||||
/// assert_eq!(fix, Fix::from_bits(3 << 8));
|
||||
/// ```
|
||||
///
|
||||
/// [`wrapping_to_fixed`]: #method.wrapping_to_fixed
|
||||
#[inline]
|
||||
fn to_fixed<F>(self) -> F
|
||||
where
|
||||
F: Fixed,
|
||||
{
|
||||
let (wrapped, overflow) = <Self as SealedInt>::overflowing_to_fixed(self);
|
||||
debug_assert!(!overflow, "{} overflows", self);
|
||||
let _ = overflow;
|
||||
wrapped
|
||||
}
|
||||
|
||||
/// Converts to a fixed-point number if it fits, otherwise returns [`None`].
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// use fixed::sealed::Int;
|
||||
/// type Fix = fixed::FixedI16<fixed::frac::U8>;
|
||||
/// assert_eq!(3.checked_to_fixed::<Fix>(), Some(Fix::from_bits(3 << 8)));
|
||||
/// assert!(i32::max_value().checked_to_fixed::<Fix>().is_none());
|
||||
/// ```
|
||||
///
|
||||
/// [`None`]: https://doc.rust-lang.org/nightly/std/option/enum.Option.html#variant.None
|
||||
#[inline]
|
||||
fn checked_to_fixed<F>(self) -> Option<F>
|
||||
where
|
||||
F: Fixed,
|
||||
{
|
||||
match <Self as SealedInt>::overflowing_to_fixed(self) {
|
||||
(wrapped, false) => Some(wrapped),
|
||||
(_, true) => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts to a fixed-point number, saturating if it does not
|
||||
/// fit.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// use fixed::sealed::Int;
|
||||
/// type Fix = fixed::FixedU16<fixed::frac::U8>;
|
||||
/// assert_eq!(3i64.saturating_to_fixed::<Fix>(), Fix::from_bits(3 << 8));
|
||||
/// assert_eq!((-1i8).saturating_to_fixed::<Fix>(), Fix::min_value());
|
||||
/// ```
|
||||
#[inline]
|
||||
fn saturating_to_fixed<F>(self) -> F
|
||||
where
|
||||
F: Fixed,
|
||||
{
|
||||
match <Self as SealedInt>::overflowing_to_fixed(self) {
|
||||
(wrapped, false) => wrapped,
|
||||
(_, true) => {
|
||||
if self.is_negative() {
|
||||
F::from_bits(<F as SealedFixed>::Bits::min_value())
|
||||
} else {
|
||||
F::from_bits(<F as SealedFixed>::Bits::max_value())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts to a fixed-point number, wrapping if it does not fit.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// use fixed::sealed::Int;
|
||||
/// type Fix = fixed::FixedU16<fixed::frac::U8>;
|
||||
/// assert_eq!(3i64.wrapping_to_fixed::<Fix>(), Fix::from_bits(3 << 8));
|
||||
/// assert_eq!((-1i8).wrapping_to_fixed::<Fix>(), Fix::from_bits(0xff00));
|
||||
/// ```
|
||||
#[inline]
|
||||
fn wrapping_to_fixed<F>(self) -> F
|
||||
where
|
||||
F: Fixed,
|
||||
{
|
||||
<Self as SealedInt>::overflowing_to_fixed(self).0
|
||||
}
|
||||
|
||||
/// Converts to a fixed-point number.
|
||||
///
|
||||
/// Returns a tuple of the fixed-point number and a [`bool`]
|
||||
/// indicating whether an overflow has occurred. On overflow, the
|
||||
/// wrapped value is returned.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// use fixed::sealed::Int;
|
||||
/// type Fix = fixed::FixedU16<fixed::frac::U8>;
|
||||
/// assert_eq!(3i64.overflowing_to_fixed::<Fix>(), (Fix::from_bits(3 << 8), false));
|
||||
/// assert_eq!((-1i8).overflowing_to_fixed::<Fix>(), (Fix::from_bits(0xff00), true));
|
||||
/// ```
|
||||
///
|
||||
///[`bool`]: https://doc.rust-lang.org/nightly/std/primitive.bool.html
|
||||
#[inline]
|
||||
fn overflowing_to_fixed<F>(self) -> (F, bool)
|
||||
where
|
||||
F: Fixed,
|
||||
{
|
||||
<Self as SealedInt>::overflowing_to_fixed(self)
|
||||
}
|
||||
}
|
||||
|
||||
/// This trait is implemented for the primitive floating-point types,
|
||||
/// and for [`f16`] if the [`f16` feature] is enabled.
|
||||
|
@ -59,7 +182,171 @@ pub trait Int: SealedInt {}
|
|||
/// [`f32`]: https://doc.rust-lang.org/nightly/std/primitive.f32.html
|
||||
/// [`f64`]: https://doc.rust-lang.org/nightly/std/primitive.f64.html
|
||||
/// [`f16` feature]: ../index.html#optional-features
|
||||
pub trait Float: SealedFloat {}
|
||||
pub trait Float: SealedFloat {
|
||||
/// Converts to a fixed-point number.
|
||||
///
|
||||
/// This method rounds to the nearest, with ties rounding to even.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the value is not [finite].
|
||||
///
|
||||
/// In debug mode, also panics if the value does not fit. In release mode
|
||||
/// the value is wrapped, but it is not considered a breaking change if in
|
||||
/// the future it panics; if wrapping is required use
|
||||
/// [`wrapping_to_fixed`] instead.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// use fixed::sealed::Float;
|
||||
/// type Fix = fixed::FixedI16<fixed::frac::U8>;
|
||||
/// // 1.625 is 1.101 in binary
|
||||
/// let fix: Fix = 1.625.to_fixed();
|
||||
/// assert_eq!(fix, Fix::from_bits(0b1101 << (8 - 3)));
|
||||
/// ```
|
||||
///
|
||||
/// [`wrapping_to_fixed`]: #method.wrapping_to_fixed
|
||||
/// [finite]: https://doc.rust-lang.org/nightly/std/primitive.f64.html#method.is_finite
|
||||
#[inline]
|
||||
fn to_fixed<F>(self) -> F
|
||||
where
|
||||
F: Fixed,
|
||||
{
|
||||
let (wrapped, overflow) = <Self as SealedFloat>::overflowing_to_fixed(self);
|
||||
debug_assert!(!overflow, "{} overflows", self);
|
||||
let _ = overflow;
|
||||
wrapped
|
||||
}
|
||||
|
||||
/// Converts to a fixed-point number if it fits, otherwise returns [`None`].
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// use fixed::sealed::Float;
|
||||
/// type Fix = fixed::FixedI16<fixed::frac::U8>;
|
||||
/// // 1.625 is 1.101 in binary
|
||||
/// let checked_fix: Option<Fix> = 1.625f32.checked_to_fixed();
|
||||
/// let one_point_625 = Fix::from_bits(0b1101 << (8 - 3));
|
||||
/// assert_eq!(checked_fix, Some(one_point_625));
|
||||
/// assert!(1000f32.checked_to_fixed::<Fix>().is_none());
|
||||
/// assert!(std::f64::NAN.checked_to_fixed::<Fix>().is_none());
|
||||
/// ```
|
||||
///
|
||||
/// [`None`]: https://doc.rust-lang.org/nightly/std/option/enum.Option.html#variant.None
|
||||
#[inline]
|
||||
fn checked_to_fixed<F>(self) -> Option<F>
|
||||
where
|
||||
F: Fixed,
|
||||
{
|
||||
if !self.is_finite() {
|
||||
return None;
|
||||
}
|
||||
match <Self as SealedFloat>::overflowing_to_fixed(self) {
|
||||
(wrapped, false) => Some(wrapped),
|
||||
(_, true) => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts to a fixed-point number, saturating if it does not
|
||||
/// fit.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This method panics if the value is [NaN].
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// use fixed::sealed::Float;
|
||||
/// type Fix = fixed::FixedI16<fixed::frac::U8>;
|
||||
/// // 1.625 is 1.101 in binary
|
||||
/// let fix: Fix = 1.625f32.saturating_to_fixed();
|
||||
/// assert_eq!(fix, Fix::from_bits(0b1101 << (8 - 3)));
|
||||
/// let neg_inf_to_fixed: Fix = std::f64::NEG_INFINITY.saturating_to_fixed();
|
||||
/// assert_eq!(neg_inf_to_fixed, Fix::min_value());
|
||||
/// ```
|
||||
///
|
||||
/// [NaN]: https://doc.rust-lang.org/nightly/std/primitive.f64.html#method.is_nan
|
||||
#[inline]
|
||||
fn saturating_to_fixed<F>(self) -> F
|
||||
where
|
||||
F: Fixed,
|
||||
{
|
||||
assert!(!self.is_nan(), "NaN");
|
||||
let saturated = if self.is_sign_negative() {
|
||||
F::from_bits(<F as SealedFixed>::Bits::min_value())
|
||||
} else {
|
||||
F::from_bits(<F as SealedFixed>::Bits::max_value())
|
||||
};
|
||||
if !self.is_finite() {
|
||||
return saturated;
|
||||
}
|
||||
match <Self as SealedFloat>::overflowing_to_fixed(self) {
|
||||
(wrapped, false) => wrapped,
|
||||
(_, true) => saturated,
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts to a fixed-point number, wrapping if it does not fit.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This method panics if the value is not [finite].
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// use fixed::sealed::Float;
|
||||
/// type Fix = fixed::FixedU16<fixed::frac::U8>;
|
||||
/// // 6.5 is 110.1 in binary
|
||||
/// let six_point_5 = Fix::from_bits(0b1101 << (8 - 1));
|
||||
/// assert_eq!(6.5f32.wrapping_to_fixed::<Fix>(), six_point_5);
|
||||
/// // 1030.5 = 1024 + 6.5, 1024 is a power of 2 that will be wrapped
|
||||
/// assert_eq!(1030.5f64.wrapping_to_fixed::<Fix>(), six_point_5);
|
||||
/// ```
|
||||
///
|
||||
/// [finite]: https://doc.rust-lang.org/nightly/std/primitive.f64.html#method.is_finite
|
||||
#[inline]
|
||||
fn wrapping_to_fixed<F>(self) -> F
|
||||
where
|
||||
F: Fixed,
|
||||
{
|
||||
<Self as SealedFloat>::overflowing_to_fixed(self).0
|
||||
}
|
||||
|
||||
/// Converts to a fixed-point number.
|
||||
///
|
||||
/// Returns a tuple of the fixed-point number and a [`bool`]
|
||||
/// indicating whether an overflow has occurred. On overflow, the
|
||||
/// wrapped value is returned.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This method panics if the value is not [finite].
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// use fixed::sealed::Float;
|
||||
/// type Fix = fixed::FixedU16<fixed::frac::U8>;
|
||||
/// // 6.5 is 110.1 in binary
|
||||
/// let six_point_5 = Fix::from_bits(0b1101 << (8 - 1));
|
||||
/// assert_eq!(6.5f32.overflowing_to_fixed::<Fix>(), (six_point_5, false));
|
||||
/// // 1030.5 = 1024 + 6.5, 1024 is a power of 2 that will be wrapped
|
||||
/// assert_eq!(1030.5f64.overflowing_to_fixed::<Fix>(), (six_point_5, true));
|
||||
/// ```
|
||||
///
|
||||
///[`bool`]: https://doc.rust-lang.org/nightly/std/primitive.bool.html
|
||||
#[inline]
|
||||
fn overflowing_to_fixed<F>(self) -> (F, bool)
|
||||
where
|
||||
F: Fixed,
|
||||
{
|
||||
<Self as SealedFloat>::overflowing_to_fixed(self)
|
||||
}
|
||||
}
|
||||
|
||||
/// This trait is implemented for all the fixed-point types.
|
||||
///
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
|
||||
use core::fmt::{Debug, Display};
|
||||
use frac::{IsLessOrEqual, True, Unsigned, U128, U16, U32, U64, U8};
|
||||
use sealed::{Fixed, SealedInt};
|
||||
use sealed::{Fixed, Float, SealedInt};
|
||||
use {
|
||||
FixedI128, FixedI16, FixedI32, FixedI64, FixedI8, FixedU128, FixedU16, FixedU32, FixedU64,
|
||||
FixedU8,
|
||||
|
@ -48,6 +48,10 @@ pub trait SealedFixed: Copy + Debug + Display {
|
|||
where
|
||||
F: Fixed;
|
||||
|
||||
fn overflowing_from_float<F>(float: F) -> (Self, bool)
|
||||
where
|
||||
F: Float;
|
||||
|
||||
#[inline]
|
||||
fn one() -> Option<Self> {
|
||||
let min_int_bits = if Self::Bits::IS_SIGNED { 2 } else { 1 };
|
||||
|
@ -95,6 +99,14 @@ macro_rules! sealed_fixed {
|
|||
$Fixed::overflowing_from_fixed(fixed)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn overflowing_from_float<F>(float: F) -> (Self, bool)
|
||||
where
|
||||
F: Float,
|
||||
{
|
||||
$Fixed::overflowing_from_float(float)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn from_bits(bits: Self::Bits) -> Self {
|
||||
$Fixed::from_bits(bits)
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
use core::fmt::{Debug, Display};
|
||||
#[cfg(feature = "f16")]
|
||||
use half::f16;
|
||||
use sealed::SealedInt;
|
||||
use sealed::{Fixed, SealedInt};
|
||||
|
||||
pub trait SealedFloat: Copy + Debug + Display {
|
||||
type Bits: SealedInt;
|
||||
|
@ -39,6 +39,10 @@ pub trait SealedFloat: Copy + Debug + Display {
|
|||
fn parts(self) -> (bool, i32, Self::Bits);
|
||||
fn from_parts(sign: bool, exp: i32, mant: Self::Bits) -> Self;
|
||||
|
||||
fn overflowing_to_fixed<F>(self) -> (F, bool)
|
||||
where
|
||||
F: Fixed;
|
||||
|
||||
fn from_neg_abs(neg: bool, abs: u128, frac_bits: u32, int_bits: u32) -> Self;
|
||||
// self must be finite, otherwise meaningless results are returned
|
||||
fn to_fixed_neg_abs_overflow(self, frac_bits: u32, int_bits: u32) -> (bool, u128, bool);
|
||||
|
@ -106,6 +110,14 @@ macro_rules! sealed_float {
|
|||
Self::from_bits(bits)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn overflowing_to_fixed<F>(self) -> (F, bool)
|
||||
where
|
||||
F: Fixed,
|
||||
{
|
||||
F::overflowing_from_float(self)
|
||||
}
|
||||
|
||||
fn from_neg_abs(neg: bool, abs: u128, frac_bits: u32, int_bits: u32) -> $Float {
|
||||
let fix_bits = frac_bits + int_bits;
|
||||
|
||||
|
|
|
@ -33,6 +33,12 @@ pub trait SealedInt: Copy + Ord + Debug + Display {
|
|||
fn overflowing_from_fixed<F>(fixed: F) -> (Self, bool)
|
||||
where
|
||||
F: Fixed;
|
||||
fn overflowing_to_fixed<F>(self) -> (F, bool)
|
||||
where
|
||||
F: Fixed;
|
||||
|
||||
fn min_value() -> Self;
|
||||
fn max_value() -> Self;
|
||||
|
||||
fn one_shl(shift: u32) -> Self;
|
||||
fn all_ones_shl(shift: u32) -> Self;
|
||||
|
@ -59,6 +65,16 @@ macro_rules! sealed_int {
|
|||
|
||||
const MSB: $Int = 1 << (Self::NBITS - 1);
|
||||
|
||||
#[inline]
|
||||
fn min_value() -> $Int {
|
||||
$Int::min_value()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn max_value() -> $Int {
|
||||
$Int::max_value()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn overflowing_from_fixed<F>(fixed: F) -> (Self, bool)
|
||||
where
|
||||
|
@ -68,6 +84,14 @@ macro_rules! sealed_int {
|
|||
(wrapped.to_bits(), overflow)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn overflowing_to_fixed<F>(self) -> (F, bool)
|
||||
where
|
||||
F: Fixed
|
||||
{
|
||||
F::overflowing_from_fixed(<$EquivFixed<U0>>::from_bits(self))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn one_shl(shift: u32) -> $Int {
|
||||
1 << shift
|
||||
|
@ -213,6 +237,16 @@ impl SealedInt for bool {
|
|||
|
||||
const MSB: bool = true;
|
||||
|
||||
#[inline]
|
||||
fn min_value() -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn max_value() -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn overflowing_from_fixed<F>(fixed: F) -> (Self, bool)
|
||||
where
|
||||
|
@ -222,6 +256,14 @@ impl SealedInt for bool {
|
|||
(wrapped.to_bits() & 0x80u8 != 0, overflow)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn overflowing_to_fixed<F>(self) -> (F, bool)
|
||||
where
|
||||
F: Fixed,
|
||||
{
|
||||
F::overflowing_from_fixed(FixedU8::<U0>::from_bits(self as u8))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn one_shl(shift: u32) -> bool {
|
||||
let _ = shift;
|
||||
|
|
Loading…
Reference in New Issue