From 5415502f07a9917451a9d17987f4f31827bd0f8e Mon Sep 17 00:00:00 2001 From: Trevor Spiteri Date: Fri, 2 Aug 2019 15:28:30 +0200 Subject: [PATCH] add CheckedFromFixed and CheckedToFixed --- src/lib.rs | 1 + src/sealed.rs | 320 +++++++++++++++++++++++++++++++++++++++++++++++++- src/traits.rs | 176 +++++++++++++++++++++++++++ 3 files changed, 494 insertions(+), 3 deletions(-) create mode 100644 src/traits.rs diff --git a/src/lib.rs b/src/lib.rs index 314a11d..6e9fcd6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -209,6 +209,7 @@ mod sealed_float; mod sealed_int; #[cfg(feature = "serde")] mod serdeize; +pub mod traits; pub mod types; mod wide_div; mod wrapping; diff --git a/src/sealed.rs b/src/sealed.rs index 40b59c8..791c562 100644 --- a/src/sealed.rs +++ b/src/sealed.rs @@ -23,6 +23,7 @@ use half::f16; pub(crate) use sealed_fixed::{SealedFixed, Widest}; pub(crate) use sealed_float::SealedFloat; pub(crate) use sealed_int::SealedInt; +use traits::{CheckedFromFixed, CheckedToFixed}; use { FixedI128, FixedI16, FixedI32, FixedI64, FixedI8, FixedU128, FixedU16, FixedU32, FixedU64, FixedU8, @@ -47,7 +48,7 @@ use { /// [`u64`]: https://doc.rust-lang.org/nightly/std/primitive.u64.html /// [`u8`]: https://doc.rust-lang.org/nightly/std/primitive.u8.html /// [`usize`]: https://doc.rust-lang.org/nightly/std/primitive.usize.html -pub trait Int: SealedInt { +pub trait Int: SealedInt + CheckedFromFixed + CheckedToFixed { /// Converts from a fixed-point number. /// /// Any fractional bits are truncated. @@ -317,7 +318,7 @@ 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 + CheckedToFixed { /// Converts from a fixed-point number. /// /// This method rounds to the nearest, with ties rounding to even. @@ -524,7 +525,7 @@ pub trait Float: SealedFloat { /// [`FixedU32`]: ../struct.FixedU32.html /// [`FixedU64`]: ../struct.FixedU64.html /// [`FixedU8`]: ../struct.FixedU8.html -pub trait Fixed: SealedFixed { +pub trait Fixed: SealedFixed + CheckedFromFixed + CheckedToFixed { /// Converts from another fixed-point number which can have a /// different type. /// @@ -886,3 +887,316 @@ impl Fixed for FixedU16 where Frac: Unsigned + IsLessOrEqual Fixed for FixedU32 where Frac: Unsigned + IsLessOrEqual {} impl Fixed for FixedU64 where Frac: Unsigned + IsLessOrEqual {} impl Fixed for FixedU128 where Frac: Unsigned + IsLessOrEqual {} + +impl CheckedToFixed for bool { + #[inline] + fn to_fixed(self) -> F + where + F: Fixed, + { + Int::to_fixed(self as u8) + } + #[inline] + fn checked_to_fixed(self) -> Option + where + F: Fixed, + { + Int::checked_to_fixed(self as u8) + } + #[inline] + fn saturating_to_fixed(self) -> F + where + F: Fixed, + { + Int::saturating_to_fixed(self as u8) + } + #[inline] + fn wrapping_to_fixed(self) -> F + where + F: Fixed, + { + Int::wrapping_to_fixed(self as u8) + } + #[inline] + fn overflowing_to_fixed(self) -> (F, bool) + where + F: Fixed, + { + Int::overflowing_to_fixed(self as u8) + } +} + +macro_rules! checked_int { + ($Int:ty) => { + impl CheckedFromFixed for $Int { + #[inline] + fn from_fixed(val: F) -> Self + where + F: Fixed, + { + Int::from_fixed(val) + } + #[inline] + fn checked_from_fixed(val: F) -> Option + where + F: Fixed, + { + Int::checked_from_fixed(val) + } + #[inline] + fn saturating_from_fixed(val: F) -> Self + where + F: Fixed, + { + Int::saturating_from_fixed(val) + } + #[inline] + fn wrapping_from_fixed(val: F) -> Self + where + F: Fixed, + { + Int::wrapping_from_fixed(val) + } + #[inline] + fn overflowing_from_fixed(val: F) -> (Self, bool) + where + F: Fixed, + { + Int::overflowing_from_fixed(val) + } + } + + impl CheckedToFixed for $Int { + #[inline] + fn to_fixed(self) -> F + where + F: Fixed, + { + Int::to_fixed(self) + } + #[inline] + fn checked_to_fixed(self) -> Option + where + F: Fixed, + { + Int::checked_to_fixed(self) + } + #[inline] + fn saturating_to_fixed(self) -> F + where + F: Fixed, + { + Int::saturating_to_fixed(self) + } + #[inline] + fn wrapping_to_fixed(self) -> F + where + F: Fixed, + { + Int::wrapping_to_fixed(self) + } + #[inline] + fn overflowing_to_fixed(self) -> (F, bool) + where + F: Fixed, + { + Int::overflowing_to_fixed(self) + } + } + }; +} + +checked_int! { i8 } +checked_int! { i16 } +checked_int! { i32 } +checked_int! { i64 } +checked_int! { i128 } +checked_int! { isize } +checked_int! { u8 } +checked_int! { u16 } +checked_int! { u32 } +checked_int! { u64 } +checked_int! { u128 } +checked_int! { usize } + +macro_rules! checked_fixed { + ($Fixed:ident, $NBits:ident) => { + impl CheckedFromFixed for $Fixed + where + Frac: Unsigned + IsLessOrEqual<$NBits, Output = True>, + { + #[inline] + fn from_fixed(val: F) -> Self + where + F: Fixed, + { + Fixed::from_fixed(val) + } + #[inline] + fn checked_from_fixed(val: F) -> Option + where + F: Fixed, + { + Fixed::checked_from_fixed(val) + } + #[inline] + fn saturating_from_fixed(val: F) -> Self + where + F: Fixed, + { + Fixed::saturating_from_fixed(val) + } + #[inline] + fn wrapping_from_fixed(val: F) -> Self + where + F: Fixed, + { + Fixed::wrapping_from_fixed(val) + } + #[inline] + fn overflowing_from_fixed(val: F) -> (Self, bool) + where + F: Fixed, + { + Fixed::overflowing_from_fixed(val) + } + } + + impl CheckedToFixed for $Fixed + where + Frac: Unsigned + IsLessOrEqual<$NBits, Output = True>, + { + #[inline] + fn to_fixed(self) -> F + where + F: Fixed, + { + Fixed::to_fixed(self) + } + #[inline] + fn checked_to_fixed(self) -> Option + where + F: Fixed, + { + Fixed::checked_to_fixed(self) + } + #[inline] + fn saturating_to_fixed(self) -> F + where + F: Fixed, + { + Fixed::saturating_to_fixed(self) + } + #[inline] + fn wrapping_to_fixed(self) -> F + where + F: Fixed, + { + Fixed::wrapping_to_fixed(self) + } + #[inline] + fn overflowing_to_fixed(self) -> (F, bool) + where + F: Fixed, + { + Fixed::overflowing_to_fixed(self) + } + } + }; +} + +checked_fixed! { FixedI8, U8 } +checked_fixed! { FixedI16, U16 } +checked_fixed! { FixedI32, U32 } +checked_fixed! { FixedI64, U64 } +checked_fixed! { FixedI128, U128 } +checked_fixed! { FixedU8, U8 } +checked_fixed! { FixedU16, U16 } +checked_fixed! { FixedU32, U32 } +checked_fixed! { FixedU64, U64 } +checked_fixed! { FixedU128, U128 } + +macro_rules! checked_float { + ($Float:ty) => { + impl CheckedFromFixed for $Float { + #[inline] + fn from_fixed(val: F) -> Self + where + F: Fixed, + { + Float::from_fixed(val) + } + #[inline] + fn checked_from_fixed(val: F) -> Option + where + F: Fixed, + { + Some(Float::from_fixed(val)) + } + #[inline] + fn saturating_from_fixed(val: F) -> Self + where + F: Fixed, + { + Float::from_fixed(val) + } + #[inline] + fn wrapping_from_fixed(val: F) -> Self + where + F: Fixed, + { + Float::from_fixed(val) + } + #[inline] + fn overflowing_from_fixed(val: F) -> (Self, bool) + where + F: Fixed, + { + (Float::from_fixed(val), false) + } + } + + impl CheckedToFixed for $Float { + #[inline] + fn to_fixed(self) -> F + where + F: Fixed, + { + Float::to_fixed(self) + } + #[inline] + fn checked_to_fixed(self) -> Option + where + F: Fixed, + { + Float::checked_to_fixed(self) + } + #[inline] + fn saturating_to_fixed(self) -> F + where + F: Fixed, + { + Float::saturating_to_fixed(self) + } + #[inline] + fn wrapping_to_fixed(self) -> F + where + F: Fixed, + { + Float::wrapping_to_fixed(self) + } + #[inline] + fn overflowing_to_fixed(self) -> (F, bool) + where + F: Fixed, + { + Float::overflowing_to_fixed(self) + } + } + }; +} + +#[cfg(feature = "f16")] +checked_float! { f16 } +checked_float! { f32 } +checked_float! { f64 } diff --git a/src/traits.rs b/src/traits.rs new file mode 100644 index 0000000..8d590c7 --- /dev/null +++ b/src/traits.rs @@ -0,0 +1,176 @@ +// Copyright © 2018–2019 Trevor Spiteri + +// This library is free software: you can redistribute it and/or +// modify it under the terms of either +// +// * the Apache License, Version 2.0 or +// * the MIT License +// +// at your option. +// +// You should have recieved copies of the Apache License and the MIT +// License along with the library. If not, see +// and +// . + +/*! +This module contains traits. + */ + +use sealed::Fixed; + +/// This trait provides checked conversions from fixed-point numbers. +/// +/// This trait is implemented for conversions between integer +/// primitives, floating-point primitives and fixed-point numbers. +/// +/// # Examples +/// +/// ```rust +/// use fixed::traits::CheckedFromFixed; +/// use fixed::types::U8F8; +/// // 0x87.65 +/// let f = U8F8::from_bits(0x8765); +/// assert_eq!(f32::from_fixed(f), f32::from(0x8765u16) / 256.0); +/// assert_eq!(i32::checked_from_fixed(f), Some(0x87)); +/// assert_eq!(u8::saturating_from_fixed(f), 0x87); +/// // no fit +/// assert_eq!(i8::checked_from_fixed(f), None); +/// assert_eq!(i8::saturating_from_fixed(f), i8::max_value()); +/// assert_eq!(i8::wrapping_from_fixed(f), 0x87u8 as i8); +/// assert_eq!(i8::overflowing_from_fixed(f), (0x87u8 as i8, true)); +/// ``` +pub trait CheckedFromFixed { + /// Converts from a fixed-point number. + /// + /// Any extra fractional bits are truncated. + /// + /// # Panics + /// + /// When debug assertions are enabled, panics if the value does + /// not fit. 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_from_fixed`] instead. + /// + /// [`wrapping_from_fixed`]: #method.wrapping_from_fixed + fn from_fixed(val: F) -> Self + where + F: Fixed; + + /// Converts from a fixed-point number if it fits, otherwise returns [`None`]. + /// + /// Any extra fractional bits are truncated. + /// + /// [`None`]: https://doc.rust-lang.org/nightly/std/option/enum.Option.html#variant.None + fn checked_from_fixed(val: F) -> Option + where + F: Fixed, + Self: Sized; + + /// Converts from a fixed-point number, saturating if it does not fit. + /// + /// Any extra fractional bits are truncated. + fn saturating_from_fixed(val: F) -> Self + where + F: Fixed; + + /// Converts from a fixed-point number, wrapping if it does not fit. + /// + /// Any extra fractional bits are truncated. + fn wrapping_from_fixed(val: F) -> Self + where + F: Fixed; + + /// Converts from a fixed-point number. + /// + /// Returns a tuple of the value and a [`bool`] indicating whether + /// an overflow has occurred. On overflow, the wrapped value is + /// returned. + /// + /// Any extra fractional bits are truncated. + /// + ///[`bool`]: https://doc.rust-lang.org/nightly/std/primitive.bool.html + fn overflowing_from_fixed(val: F) -> (Self, bool) + where + F: Fixed, + Self: Sized; +} + +/// This trait provides checked conversions to fixed-point numbers. +/// +/// This trait is implemented for conversions between integer +/// primitives, floating-point primitives and fixed-point numbers. +/// +/// # Examples +/// +/// ```rust +/// use fixed::traits::CheckedToFixed; +/// use fixed::types::{U8F8, U16F16}; +/// let f: U8F8 = 13.5f32.to_fixed(); +/// assert_eq!(f, U8F8::from_bits((13 << 8) | (1 << 7))); +/// // 0x1234.5678 is too large and can be wrapped to 0x34.56 +/// let too_large = U16F16::from_bits(0x1234_5678); +/// let checked: Option = too_large.checked_to_fixed(); +/// assert_eq!(checked, None); +/// let saturating: U8F8 = too_large.saturating_to_fixed(); +/// assert_eq!(saturating, U8F8::max_value()); +/// let wrapping: U8F8 = too_large.wrapping_to_fixed(); +/// assert_eq!(wrapping, U8F8::from_bits(0x3456)); +/// let overflowing: (U8F8, bool) = too_large.overflowing_to_fixed(); +/// assert_eq!(overflowing, (U8F8::from_bits(0x3456), true)); +/// ``` +pub trait CheckedToFixed { + /// Converts to a fixed-point number. + /// + /// Any extra fractional bits are truncated. + /// + /// # Panics + /// + /// When debug assertions are enabled, panics if the value does + /// not fit. 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_to_fixed`] instead. + /// + /// [`wrapping_to_fixed`]: #method.wrapping_to_fixed + fn to_fixed(self) -> F + where + F: Fixed; + + /// Converts to a fixed-point number if it fits, otherwise returns [`None`]. + /// + /// Any extra fractional bits are truncated. + /// + /// [`None`]: https://doc.rust-lang.org/nightly/std/option/enum.Option.html#variant.None + fn checked_to_fixed(self) -> Option + where + F: Fixed; + + /// Converts to a fixed-point number, saturating if it does not fit. + /// + /// Any extra fractional bits are truncated. + fn saturating_to_fixed(self) -> F + where + F: Fixed; + + /// Converts to a fixed-point number, wrapping if it does not fit. + /// + /// Any extra fractional bits are truncated. + fn wrapping_to_fixed(self) -> F + where + F: Fixed; + + /// Converts from 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. + /// + /// Any extra fractional bits are truncated. + /// + ///[`bool`]: https://doc.rust-lang.org/nightly/std/primitive.bool.html + fn overflowing_to_fixed(self) -> (F, bool) + where + F: Fixed; +}