add LosslessTry{From,Into} and immplement them for Fixed <-> Fixed

This commit is contained in:
Trevor Spiteri 2020-05-08 12:41:35 +02:00
parent f23ddc6616
commit bc5e01120f
5 changed files with 149 additions and 13 deletions

View File

@ -59,6 +59,10 @@ The conversions supported cover the following cases.
numeric primitives are provided using the [`LossyFrom`] and
[`LossyInto`] traits. The source can have more fractional bits
than the destination.
* Checked lossless conversions between fixed-point numbers and
numeric primitives are provided using the [`LosslessTryFrom`] and
[`LosslessTryInto`] traits. The source cannot have more fractional
bits than the destination.
* Checked conversions between fixed-point numbers and numeric
primitives are provided using the [`FromFixed`] and [`ToFixed`]
traits, or using the [`from_num`] and [`to_num`] methods and
@ -77,14 +81,18 @@ The conversions supported cover the following cases.
### Version 1.0.0 news (unreleased)
* The crate now requires rustc version 1.43.0 or later.
* All deprecated items were removed.
* The [`LosslessTryFrom`][ltf-1-0] and [`LosslessTryInto`][lti-1-0]
traits were added.
* The [`PHI`][phi-1-0] and [`FRAC_1_PHI`][f1phi-1-0] constants were
added to the [`consts`][cons-1-0] module and as
[associated constants][f-phi-1-0] for fixed-point types.
* All deprecated items were removed.
[cons-1-0]: https://tspiteri.gitlab.io/fixed/dev/fixed/consts/index.html
[f-phi-1-0]: https://tspiteri.gitlab.io/fixed/dev/fixed/struct.FixedI32.html#associatedconstant.PHI
[f1phi-1-0]: https://tspiteri.gitlab.io/fixed/dev/fixed/consts/constant.FRAC_1_PHI.html
[ltf-1-0]: https://tspiteri.gitlab.io/fixed/dev/fixed/traits/trait.LosslessTryFrom.html
[lti-1-0]: https://tspiteri.gitlab.io/fixed/dev/fixed/traits/trait.LosslessTryInto.html
[phi-1-0]: https://tspiteri.gitlab.io/fixed/dev/fixed/consts/constant.PHI.html
### Version 0.5.6 news (2020-05-01)
@ -254,6 +262,8 @@ additional terms or conditions.
[`I4F12`]: https://docs.rs/fixed/0.5.6/fixed/types/type.I4F12.html
[`I4F4`]: https://docs.rs/fixed/0.5.6/fixed/types/type.I4F4.html
[`Into`]: https://doc.rust-lang.org/nightly/core/convert/trait.Into.html
[`LosslessTryFrom`]: https://tspiteri.gitlab.io/fixed/dev/fixed/traits/trait.LosslessTryFrom.html
[`LosslessTryInto`]: https://tspiteri.gitlab.io/fixed/dev/fixed/traits/trait.LosslessTryInto.html
[`LossyFrom`]: https://docs.rs/fixed/0.5.6/fixed/traits/trait.LossyFrom.html
[`LossyInto`]: https://docs.rs/fixed/0.5.6/fixed/traits/trait.LossyInto.html
[`LowerHex`]: https://doc.rust-lang.org/nightly/core/fmt/trait.LowerHex.html

View File

@ -9,14 +9,18 @@ Version 1.0.0 (unreleased)
==========================
* The crate now requires rustc version 1.43.0 or later.
* All deprecated items were removed.
* The [`LosslessTryFrom`][ltf-1-0] and [`LosslessTryInto`][lti-1-0]
traits were added.
* The [`PHI`][phi-1-0] and [`FRAC_1_PHI`][f1phi-1-0] constants were
added to the [`consts`][cons-1-0] module and as
[associated constants][f-phi-1-0] for fixed-point types.
* All deprecated items were removed.
[cons-1-0]: https://tspiteri.gitlab.io/fixed/dev/fixed/consts/index.html
[f-phi-1-0]: https://tspiteri.gitlab.io/fixed/dev/fixed/struct.FixedI32.html#associatedconstant.PHI
[f1phi-1-0]: https://tspiteri.gitlab.io/fixed/dev/fixed/consts/constant.FRAC_1_PHI.html
[ltf-1-0]: https://tspiteri.gitlab.io/fixed/dev/fixed/traits/trait.LosslessTryFrom.html
[lti-1-0]: https://tspiteri.gitlab.io/fixed/dev/fixed/traits/trait.LosslessTryInto.html
[phi-1-0]: https://tspiteri.gitlab.io/fixed/dev/fixed/consts/constant.PHI.html
Version 0.5.6 (2020-05-01)

View File

@ -15,7 +15,7 @@
use crate::{
helpers::IntHelper,
traits::LossyFrom,
traits::{LosslessTryFrom, LossyFrom},
types::extra::{
Diff, IsLessOrEqual, LeEqU128, LeEqU16, LeEqU32, LeEqU64, LeEqU8, True, U0, U1, U127, U128,
U15, U16, U31, U32, U63, U64, U7, U8,
@ -91,10 +91,47 @@ macro_rules! convert {
};
}
macro_rules! convert_lossless {
(
($Src:ident, $SrcBits:ident, $SrcLeEqU:ident) ->
($Dst:ident, $DstBits:ident, $DstLeEqU:ident)
) => {
// lossless because Src::FRAC_NBITS <= Dst::FRAC_NBITS
impl<FracSrc: $SrcLeEqU, FracDst: $DstLeEqU> LosslessTryFrom<$Src<FracSrc>>
for $Dst<FracDst>
where
FracSrc: IsLessOrEqual<FracDst, Output = True>,
{
/// Converts a fixed-pint number.
///
/// This conversion may fail (fallible) but does not lose
/// precision (lossless).
#[inline]
fn lossless_try_from(src: $Src<FracSrc>) -> Option<Self> {
src.checked_to_num()
}
}
};
($Src:ident, $SrcBits:ident, $SrcLeEqU:ident) => {
convert_lossless! { ($Src, $SrcBits, $SrcLeEqU) -> (FixedI8, U8, LeEqU8) }
convert_lossless! { ($Src, $SrcBits, $SrcLeEqU) -> (FixedI16, U16, LeEqU16) }
convert_lossless! { ($Src, $SrcBits, $SrcLeEqU) -> (FixedI32, U32, LeEqU32) }
convert_lossless! { ($Src, $SrcBits, $SrcLeEqU) -> (FixedI64, U64, LeEqU64) }
convert_lossless! { ($Src, $SrcBits, $SrcLeEqU) -> (FixedI128, U128, LeEqU128) }
convert_lossless! { ($Src, $SrcBits, $SrcLeEqU) -> (FixedU8, U8, LeEqU8) }
convert_lossless! { ($Src, $SrcBits, $SrcLeEqU) -> (FixedU16, U16, LeEqU16) }
convert_lossless! { ($Src, $SrcBits, $SrcLeEqU) -> (FixedU32, U32, LeEqU32) }
convert_lossless! { ($Src, $SrcBits, $SrcLeEqU) -> (FixedU64, U64, LeEqU64) }
convert_lossless! { ($Src, $SrcBits, $SrcLeEqU) -> (FixedU128, U128, LeEqU128) }
};
}
macro_rules! convert_lossy {
(
($SrcU:ident, $SrcI:ident, $SrcBits:ident, $SrcLeEqU:ident) ->
($DstU:ident, $DstI:ident, $DstBits:ident, $DstBitsM1:ident, $DstLeEqU:ident)) => {
($DstU:ident, $DstI:ident, $DstBits:ident, $DstBitsM1:ident, $DstLeEqU:ident)
) => {
// unsigned -> unsigned, infallible because Src::INT_NBITS <= Dst::INT_NBITS
impl<FracSrc: $SrcLeEqU, FracDst: $DstLeEqU> LossyFrom<$SrcU<FracSrc>> for $DstU<FracDst>
where
$SrcBits: Sub<FracSrc>,
@ -113,6 +150,7 @@ macro_rules! convert_lossy {
}
}
// signed -> signed, infallible because Src::INT_NBITS <= Dst::INT_NBITS
impl<FracSrc: $SrcLeEqU, FracDst: $DstLeEqU> LossyFrom<$SrcI<FracSrc>> for $DstI<FracDst>
where
$SrcBits: Sub<FracSrc>,
@ -131,6 +169,7 @@ macro_rules! convert_lossy {
}
}
// signed -> signed, infallible because Src::INT_NBITS <= Dst::INT_NBITS - 1
impl<FracSrc: $SrcLeEqU, FracDst: $DstLeEqU> LossyFrom<$SrcU<FracSrc>> for $DstI<FracDst>
where
$SrcBits: Sub<FracSrc>,
@ -182,6 +221,17 @@ convert! { (FixedU32, FixedI32, U32, LeEqU32) -> (FixedU128, FixedI128, U128, U1
convert! { (FixedU64, FixedI64, U64, LeEqU64) -> (FixedU128, FixedI128, U128, U127, LeEqU128) }
convert_lossless! { FixedI8, U8, LeEqU8 }
convert_lossless! { FixedI16, U16, LeEqU16 }
convert_lossless! { FixedI32, U32, LeEqU32 }
convert_lossless! { FixedI64, U64, LeEqU64 }
convert_lossless! { FixedI128, U128, LeEqU128 }
convert_lossless! { FixedU8, U8, LeEqU8 }
convert_lossless! { FixedU16, U16, LeEqU16 }
convert_lossless! { FixedU32, U32, LeEqU32 }
convert_lossless! { FixedU64, U64, LeEqU64 }
convert_lossless! { FixedU128, U128, LeEqU128 }
convert_lossy! { FixedU8, FixedI8, U8, LeEqU8 }
convert_lossy! { FixedU16, FixedI16, U16, LeEqU16 }
convert_lossy! { FixedU32, FixedI32, U32, LeEqU32 }

View File

@ -68,6 +68,10 @@ The conversions supported cover the following cases.
numeric primitives are provided using the [`LossyFrom`] and
[`LossyInto`] traits. The source can have more fractional bits
than the destination.
* Checked lossless conversions between fixed-point numbers and
numeric primitives are provided using the [`LosslessTryFrom`] and
[`LosslessTryInto`] traits. The source cannot have more fractional
bits than the destination.
* Checked conversions between fixed-point numbers and numeric
primitives are provided using the [`FromFixed`] and [`ToFixed`]
traits, or using the [`from_num`] and [`to_num`] methods and
@ -218,6 +222,8 @@ additional terms or conditions.
[`I4F12`]: types/type.I4F12.html
[`I4F4`]: types/type.I4F4.html
[`Into`]: https://doc.rust-lang.org/nightly/core/convert/trait.Into.html
[`LosslessTryFrom`]: traits/trait.LosslessTryFrom.html
[`LosslessTryInto`]: traits/trait.LosslessTryInto.html
[`LossyFrom`]: traits/trait.LossyFrom.html
[`LossyInto`]: traits/trait.LossyInto.html
[`LowerHex`]: https://doc.rust-lang.org/nightly/core/fmt/trait.LowerHex.html
@ -303,7 +309,9 @@ use core::{
///
/// [prelude]: https://doc.rust-lang.org/nightly/std/prelude/index.html
pub mod prelude {
pub use crate::traits::{FromFixed, LossyFrom, LossyInto, ToFixed};
pub use crate::traits::{
FromFixed, LosslessTryFrom, LosslessTryInto, LossyFrom, LossyInto, ToFixed,
};
}
#[macro_use]

View File

@ -1153,6 +1153,70 @@ pub trait FixedUnsigned: Fixed {
fn checked_next_power_of_two(self) -> Option<Self>;
}
/// This trait provides lossless conversions that might be fallible.
///
/// This trait is implemented for conversions between integer
/// primitives, floating-point primitives and fixed-point numbers.
///
/// # Examples
///
/// ```rust
/// use fixed::traits::LosslessTryFrom;
/// use fixed::types::{I4F12, I24F8};
/// // original is 0x000001.23, lossless is 0x1.230
/// let original = I24F8::from_bits(0x0000_0123);
/// let lossless = I4F12::lossless_try_from(original);
/// assert_eq!(lossless, Some(I4F12::from_bits(0x1230)));
/// // too_large is 0x000012.34, 0x12.340 does not fit in I4F12
/// let too_large = I24F8::from_bits(0x0000_1234);
/// let overflow = I4F12::lossless_try_from(too_large);
/// assert_eq!(overflow, None);
/// ```
pub trait LosslessTryFrom<Src>: Sized {
/// Performs the conversion.
fn lossless_try_from(src: Src) -> Option<Self>;
}
/// This trait provides lossless conversions that might be fallible.
/// This is the reciprocal of [`LosslessTryFrom`].
///
/// Usually [`LosslessTryFrom`] should be implemented instead of this
/// trait; there is a blanket implementation which provides this trait
/// when [`LosslessTryFrom`] is implemented (similar to [`Into`] and
/// [`From`]).
///
/// # Examples
///
/// ```rust
/// use fixed::traits::LosslessTryInto;
/// use fixed::types::{I4F12, I24F8};
/// // original is 0x000001.23, lossless is 0x1.230
/// let original = I24F8::from_bits(0x0000_0123);
/// let lossless: Option<I4F12> = original.lossless_try_into();
/// assert_eq!(lossless, Some(I4F12::from_bits(0x1230)));
/// // too_large is 0x000012.34, 0x12.340 does not fit in I4F12
/// let too_large = I24F8::from_bits(0x0000_1234);
/// let overflow: Option<I4F12> = too_large.lossless_try_into();
/// assert_eq!(overflow, None);
/// ```
///
/// [`From`]: https://doc.rust-lang.org/nightly/core/convert/trait.From.html
/// [`Into`]: https://doc.rust-lang.org/nightly/core/convert/trait.Into.html
/// [`LosslessTryFrom`]: trait.LosslessTryFrom.html
pub trait LosslessTryInto<Dst> {
/// Performs the conversion.
fn lossless_try_into(self) -> Option<Dst>;
}
impl<Src, Dst> LosslessTryInto<Dst> for Src
where
Dst: LosslessTryFrom<Src>,
{
fn lossless_try_into(self) -> Option<Dst> {
Dst::lossless_try_from(self)
}
}
/// This trait provides infallible conversions that might be lossy.
///
/// This trait is implemented for conversions between integer
@ -1162,11 +1226,11 @@ pub trait FixedUnsigned: Fixed {
///
/// ```rust
/// use fixed::traits::LossyFrom;
/// use fixed::types::{I12F4, I4F60};
/// // original is 0x1.234
/// let original = I4F60::from_bits(0x1234i64 << (60 - 12));
/// use fixed::types::{I12F4, I8F24};
/// // original is 0x12.345678, lossy is 0x012.3
/// let original = I8F24::from_bits(0x1234_5678);
/// let lossy = I12F4::lossy_from(original);
/// assert_eq!(lossy, I12F4::from_bits(0x0012));
/// assert_eq!(lossy, I12F4::from_bits(0x0123));
/// ```
pub trait LossyFrom<Src> {
/// Performs the conversion.
@ -1184,11 +1248,11 @@ pub trait LossyFrom<Src> {
///
/// ```rust
/// use fixed::traits::LossyInto;
/// use fixed::types::{I12F4, I4F12};
/// // original is 0x1.234
/// let original = I4F12::from_bits(0x1234);
/// use fixed::types::{I12F4, I8F24};
/// // original is 0x12.345678, lossy is 0x012.3
/// let original = I8F24::from_bits(0x1234_5678);
/// let lossy: I12F4 = original.lossy_into();
/// assert_eq!(lossy, I12F4::from_bits(0x0012));
/// assert_eq!(lossy, I12F4::from_bits(0x0123));
/// ```
///
/// [`From`]: https://doc.rust-lang.org/nightly/core/convert/trait.From.html