add to_fixed plus checked versions to traits Int and Float

This commit is contained in:
Trevor Spiteri 2019-02-01 02:40:35 +01:00
parent c9e9686ac4
commit f7300c7f8a
7 changed files with 374 additions and 11 deletions

View File

@ -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)

View File

@ -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)
==========================

View File

@ -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 {

View File

@ -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.
///

View File

@ -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)

View File

@ -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;

View File

@ -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;