fixed/src/convert.rs

1053 lines
39 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Copyright © 20182019 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
// <https://www.apache.org/licenses/LICENSE-2.0> and
// <https://opensource.org/licenses/MIT>.
#[cfg(feature = "f16")]
use half::f16;
use {
crate::{
frac::{
IsGreaterOrEqual, IsLessOrEqual, True, Unsigned, U0, U1, U128, U16, U2, U32, U64, U8,
},
sealed::SealedInt,
traits::{FromFixed, LossyFrom},
FixedI128, FixedI16, FixedI32, FixedI64, FixedI8, FixedU128, FixedU16, FixedU32, FixedU64,
FixedU8,
},
core::ops::{Add, Sub},
};
macro_rules! convert {
(($SrcU:ident, $SrcI:ident, $SrcBits:ident) -> ($DstU:ident, $DstI:ident, $DstBits:ident)) => {
// Conditions: FracSrc <= FracDst <= FracSrc + ($DstBits - $SrcBits)
impl<FracSrc, FracDst, FracMax> From<$SrcU<FracSrc>> for $DstU<FracDst>
where
FracSrc: Unsigned
+ IsLessOrEqual<$SrcBits, Output = True>
+ Add<<$DstBits as Sub<$SrcBits>>::Output, Output = FracMax>,
FracDst: Unsigned
+ IsLessOrEqual<$DstBits, Output = True>
+ IsGreaterOrEqual<FracSrc, Output = True>
+ IsLessOrEqual<FracMax, Output = True>,
FracMax: Unsigned,
{
#[inline]
fn from(src: $SrcU<FracSrc>) -> $DstU<FracDst> {
let unshifted = $DstU::<FracDst>::from_bits(src.to_bits().into()).to_bits();
let shift = FracDst::U32 - FracSrc::U32;
$DstU::<FracDst>::from_bits(unshifted << shift)
}
}
// Conditions: FracSrc <= FracDst <= FracSrc + ($DstBits - $SrcBits)
impl<FracSrc, FracDst, FracMax> From<$SrcI<FracSrc>> for $DstI<FracDst>
where
FracSrc: Unsigned
+ IsLessOrEqual<$SrcBits, Output = True>
+ Add<<$DstBits as Sub<$SrcBits>>::Output, Output = FracMax>,
FracDst: Unsigned
+ IsLessOrEqual<$DstBits, Output = True>
+ IsGreaterOrEqual<FracSrc, Output = True>
+ IsLessOrEqual<FracMax, Output = True>,
FracMax: Unsigned,
{
#[inline]
fn from(src: $SrcI<FracSrc>) -> $DstI<FracDst> {
let unshifted = $DstI::<FracDst>::from_bits(src.to_bits().into()).to_bits();
let shift = FracDst::U32 - FracSrc::U32;
$DstI::<FracDst>::from_bits(unshifted << shift)
}
}
// Conditions: FracSrc <= FracDst <= FracSrc + ($DstBits - $SrcBits - 1)
impl<FracSrc, FracDst, FracMax> From<$SrcU<FracSrc>> for $DstI<FracDst>
where
FracSrc: Unsigned
+ IsLessOrEqual<$SrcBits, Output = True>
+ Add<
<<$DstBits as Sub<$SrcBits>>::Output as Sub<U1>>::Output,
Output = FracMax,
>,
FracDst: Unsigned
+ IsLessOrEqual<$DstBits, Output = True>
+ IsGreaterOrEqual<FracSrc, Output = True>
+ IsLessOrEqual<FracMax, Output = True>,
FracMax: Unsigned,
{
#[inline]
fn from(src: $SrcU<FracSrc>) -> $DstI<FracDst> {
let unshifted = $DstI::<FracDst>::from_bits(src.to_bits().into()).to_bits();
let shift = FracDst::U32 - FracSrc::U32;
$DstI::<FracDst>::from_bits(unshifted << shift)
}
}
};
}
macro_rules! convert_lossy {
(($SrcU:ident, $SrcI:ident, $SrcBits:ident) -> ($DstU:ident, $DstI:ident, $DstBits:ident)) => {
// Conditions: FracDst + $SrcBits <= FracSrc + $DstBits
impl<FracSrc, FracDst, FracSrcP, FracDstP> LossyFrom<$SrcU<FracSrc>> for $DstU<FracDst>
where
FracSrc: Unsigned
+ IsLessOrEqual<$SrcBits, Output = True>
+ Add<$DstBits, Output = FracSrcP>,
FracDst: Unsigned
+ IsLessOrEqual<$DstBits, Output = True>
+ Add<$SrcBits, Output = FracDstP>,
FracSrcP: Unsigned,
FracDstP: Unsigned + IsLessOrEqual<FracSrcP, Output = True>,
{
#[inline]
fn lossy_from(src: $SrcU<FracSrc>) -> $DstU<FracDst> {
FromFixed::wrapping_from_fixed(src)
}
}
// Conditions: FracDst + $SrcBits <= FracSrc + $DstBits
impl<FracSrc, FracDst, FracSrcP, FracDstP> LossyFrom<$SrcI<FracSrc>> for $DstI<FracDst>
where
FracSrc: Unsigned
+ IsLessOrEqual<$SrcBits, Output = True>
+ Add<$DstBits, Output = FracSrcP>,
FracDst: Unsigned
+ IsLessOrEqual<$DstBits, Output = True>
+ Add<$SrcBits, Output = FracDstP>,
FracSrcP: Unsigned,
FracDstP: Unsigned + IsLessOrEqual<FracSrcP, Output = True>,
{
#[inline]
fn lossy_from(src: $SrcI<FracSrc>) -> $DstI<FracDst> {
FromFixed::wrapping_from_fixed(src)
}
}
// Conditions: FracDst + $SrcBits + 1 <= FracSrc + $DstBits
impl<FracSrc, FracDst, FracSrcP, FracDstP> LossyFrom<$SrcU<FracSrc>> for $DstI<FracDst>
where
FracSrc: Unsigned
+ IsLessOrEqual<$SrcBits, Output = True>
+ Add<$DstBits, Output = FracSrcP>,
FracDst: Unsigned
+ IsLessOrEqual<$DstBits, Output = True>
+ Add<<$SrcBits as Add<U1>>::Output, Output = FracDstP>,
FracSrcP: Unsigned,
FracDstP: Unsigned + IsLessOrEqual<FracSrcP, Output = True>,
{
#[inline]
fn lossy_from(src: $SrcU<FracSrc>) -> $DstI<FracDst> {
FromFixed::wrapping_from_fixed(src)
}
}
};
($SrcU:ident, $SrcI:ident, $SrcBits:ident) => {
convert_lossy! { ($SrcU, $SrcI, $SrcBits) -> (FixedU8, FixedI8, U8) }
convert_lossy! { ($SrcU, $SrcI, $SrcBits) -> (FixedU16, FixedI16, U16) }
convert_lossy! { ($SrcU, $SrcI, $SrcBits) -> (FixedU32, FixedI32, U32) }
convert_lossy! { ($SrcU, $SrcI, $SrcBits) -> (FixedU64, FixedI64, U64) }
convert_lossy! { ($SrcU, $SrcI, $SrcBits) -> (FixedU128, FixedI128, U128) }
};
}
convert! { (FixedU8, FixedI8, U8) -> (FixedU16, FixedI16, U16) }
convert! { (FixedU8, FixedI8, U8) -> (FixedU32, FixedI32, U32) }
convert! { (FixedU8, FixedI8, U8) -> (FixedU64, FixedI64, U64) }
convert! { (FixedU8, FixedI8, U8) -> (FixedU128, FixedI128, U128) }
convert! { (FixedU16, FixedI16, U16) -> (FixedU32, FixedI32, U32) }
convert! { (FixedU16, FixedI16, U16) -> (FixedU64, FixedI64, U64) }
convert! { (FixedU16, FixedI16, U16) -> (FixedU128, FixedI128, U128) }
convert! { (FixedU32, FixedI32, U32) -> (FixedU64, FixedI64, U64) }
convert! { (FixedU32, FixedI32, U32) -> (FixedU128, FixedI128, U128) }
convert! { (FixedU64, FixedI64, U64) -> (FixedU128, FixedI128, U128) }
convert_lossy! { FixedU8, FixedI8, U8 }
convert_lossy! { FixedU16, FixedI16, U16 }
convert_lossy! { FixedU32, FixedI32, U32 }
convert_lossy! { FixedU64, FixedI64, U64 }
convert_lossy! { FixedU128, FixedI128, U128 }
macro_rules! int_to_fixed {
(($SrcU:ident, $SrcI:ident, $SrcBits:ident) -> ($DstU:ident, $DstI:ident, $DstBits:ident)) => {
// Condition: FracDst <= $DstBits - $SrcBits
impl<FracDst> From<$SrcU> for $DstU<FracDst>
where
FracDst: Unsigned
+ IsLessOrEqual<$DstBits, Output = True>
+ IsLessOrEqual<<$DstBits as Sub<$SrcBits>>::Output, Output = True>,
{
#[inline]
fn from(src: $SrcU) -> $DstU<FracDst> {
let unshifted = $DstU::<FracDst>::from_bits(src.into()).to_bits();
let shift = FracDst::U32;
$DstU::<FracDst>::from_bits(unshifted << shift)
}
}
// Condition: FracDst <= $DstBits - $SrcBits
impl<FracDst> From<$SrcI> for $DstI<FracDst>
where
FracDst: Unsigned
+ IsLessOrEqual<$DstBits, Output = True>
+ IsLessOrEqual<<$DstBits as Sub<$SrcBits>>::Output, Output = True>,
{
#[inline]
fn from(src: $SrcI) -> $DstI<FracDst> {
let unshifted = $DstI::<FracDst>::from_bits(src.into()).to_bits();
let shift = FracDst::U32;
$DstI::<FracDst>::from_bits(unshifted << shift)
}
}
// Condition: FracDst <= $DstBits - $SrcBits - 1
impl<FracDst> From<$SrcU> for $DstI<FracDst>
where
FracDst: Unsigned
+ IsLessOrEqual<$DstBits, Output = True>
+ IsLessOrEqual<
<<$DstBits as Sub<$SrcBits>>::Output as Sub<U1>>::Output,
Output = True,
>,
{
#[inline]
fn from(src: $SrcU) -> $DstI<FracDst> {
let unshifted = $DstI::<FracDst>::from_bits(src.into()).to_bits();
let shift = FracDst::U32;
$DstI::<FracDst>::from_bits(unshifted << shift)
}
}
// Condition: FracDst <= $DstBits - $SrcBits
impl<FracDst> LossyFrom<$SrcU> for $DstU<FracDst>
where
FracDst: Unsigned
+ IsLessOrEqual<$DstBits, Output = True>
+ IsLessOrEqual<<$DstBits as Sub<$SrcBits>>::Output, Output = True>,
{
#[inline]
fn lossy_from(src: $SrcU) -> $DstU<FracDst> {
From::from(src)
}
}
// Condition: FracDst <= $DstBits - $SrcBits
impl<FracDst> LossyFrom<$SrcI> for $DstI<FracDst>
where
FracDst: Unsigned
+ IsLessOrEqual<$DstBits, Output = True>
+ IsLessOrEqual<<$DstBits as Sub<$SrcBits>>::Output, Output = True>,
{
#[inline]
fn lossy_from(src: $SrcI) -> $DstI<FracDst> {
From::from(src)
}
}
// Condition: FracDst <= $DstBits - $SrcBits - 1
impl<FracDst> LossyFrom<$SrcU> for $DstI<FracDst>
where
FracDst: Unsigned
+ IsLessOrEqual<$DstBits, Output = True>
+ IsLessOrEqual<
<<$DstBits as Sub<$SrcBits>>::Output as Sub<U1>>::Output,
Output = True,
>,
{
#[inline]
fn lossy_from(src: $SrcU) -> $DstI<FracDst> {
From::from(src)
}
}
};
(($SrcU:ident, $SrcI:ident) -> ($DstU:ident, $DstI:ident)) => {
impl From<$SrcU> for $DstU<U0> {
#[inline]
fn from(src: $SrcU) -> $DstU<U0> {
$DstU::<U0>::from_bits(src)
}
}
impl From<$SrcI> for $DstI<U0> {
#[inline]
fn from(src: $SrcI) -> $DstI<U0> {
$DstI::<U0>::from_bits(src)
}
}
impl LossyFrom<$SrcU> for $DstU<U0> {
#[inline]
fn lossy_from(src: $SrcU) -> $DstU<U0> {
From::from(src)
}
}
impl LossyFrom<$SrcI> for $DstI<U0> {
#[inline]
fn lossy_from(src: $SrcI) -> $DstI<U0> {
From::from(src)
}
}
};
}
int_to_fixed! { (u8, i8) -> (FixedU8, FixedI8) }
int_to_fixed! { (u8, i8, U8) -> (FixedU16, FixedI16, U16) }
int_to_fixed! { (u8, i8, U8) -> (FixedU32, FixedI32, U32) }
int_to_fixed! { (u8, i8, U8) -> (FixedU64, FixedI64, U64) }
int_to_fixed! { (u8, i8, U8) -> (FixedU128, FixedI128, U128) }
int_to_fixed! { (u16, i16) -> (FixedU16, FixedI16) }
int_to_fixed! { (u16, i16, U16) -> (FixedU32, FixedI32, U32) }
int_to_fixed! { (u16, i16, U16) -> (FixedU64, FixedI64, U64) }
int_to_fixed! { (u16, i16, U16) -> (FixedU128, FixedI128, U128) }
int_to_fixed! { (u32, i32) -> (FixedU32, FixedI32) }
int_to_fixed! { (u32, i32, U32) -> (FixedU64, FixedI64, U64) }
int_to_fixed! { (u32, i32, U32) -> (FixedU128, FixedI128, U128) }
int_to_fixed! { (u64, i64) -> (FixedU64, FixedI64) }
int_to_fixed! { (u64, i64, U64) -> (FixedU128, FixedI128, U128) }
int_to_fixed! { (u128, i128) -> (FixedU128, FixedI128) }
macro_rules! bool_to_fixed {
($DstU:ident, $DstI:ident, $DstBits:ident) => {
// Condition: FracDst <= $DstBits - 1
impl<FracDst> From<bool> for $DstU<FracDst>
where
FracDst: Unsigned
+ IsLessOrEqual<$DstBits, Output = True>
+ IsLessOrEqual<<$DstBits as Sub<U1>>::Output, Output = True>,
{
#[inline]
fn from(src: bool) -> $DstU<FracDst> {
let unshifted = $DstU::<FracDst>::from_bits(src.into()).to_bits();
let shift = FracDst::U32;
$DstU::<FracDst>::from_bits(unshifted << shift)
}
}
// Condition: FracDst <= $DstBits - 2
impl<FracDst> From<bool> for $DstI<FracDst>
where
FracDst: Unsigned
+ IsLessOrEqual<$DstBits, Output = True>
+ IsLessOrEqual<<$DstBits as Sub<U2>>::Output, Output = True>,
{
#[inline]
fn from(src: bool) -> $DstI<FracDst> {
let unshifted = $DstI::<FracDst>::from_bits(src.into()).to_bits();
let shift = FracDst::U32;
$DstI::<FracDst>::from_bits(unshifted << shift)
}
}
};
}
bool_to_fixed! { FixedU8, FixedI8, U8 }
bool_to_fixed! { FixedU16, FixedI16, U16 }
bool_to_fixed! { FixedU32, FixedI32, U32 }
bool_to_fixed! { FixedU64, FixedI64, U64 }
bool_to_fixed! { FixedU128, FixedI128, U128 }
macro_rules! fixed_to_int {
(($SrcU:ident, $SrcI:ident) -> ($DstU:ident, $DstI:ident)) => {
impl From<$SrcU<U0>> for $DstU {
#[inline]
fn from(src: $SrcU<U0>) -> $DstU {
src.to_bits().into()
}
}
impl From<$SrcI<U0>> for $DstI {
#[inline]
fn from(src: $SrcI<U0>) -> $DstI {
src.to_bits().into()
}
}
};
(($SrcU:ident, $SrcI:ident) -> wider ($DstU:ident, $DstI:ident)) => {
fixed_to_int! { ($SrcU, $SrcI) -> ($DstU, $DstI) }
impl From<$SrcU<U0>> for $DstI {
#[inline]
fn from(src: $SrcU<U0>) -> $DstI {
src.to_bits().into()
}
}
};
}
fixed_to_int! { (FixedU8, FixedI8) -> (u8, i8) }
fixed_to_int! { (FixedU8, FixedI8) -> wider (u16, i16) }
fixed_to_int! { (FixedU8, FixedI8) -> wider (u32, i32) }
fixed_to_int! { (FixedU8, FixedI8) -> wider (u64, i64) }
fixed_to_int! { (FixedU8, FixedI8) -> wider (u128, i128) }
fixed_to_int! { (FixedU8, FixedI8) -> wider (usize, isize) }
fixed_to_int! { (FixedU16, FixedI16) -> (u16, i16) }
fixed_to_int! { (FixedU16, FixedI16) -> wider (u32, i32) }
fixed_to_int! { (FixedU16, FixedI16) -> wider (u64, i64) }
fixed_to_int! { (FixedU16, FixedI16) -> wider (u128, i128) }
fixed_to_int! { (FixedU16, FixedI16) -> (usize, isize) }
fixed_to_int! { (FixedU32, FixedI32) -> (u32, i32) }
fixed_to_int! { (FixedU32, FixedI32) -> wider (u64, i64) }
fixed_to_int! { (FixedU32, FixedI32) -> wider (u128, i128) }
fixed_to_int! { (FixedU64, FixedI64) -> (u64, i64) }
fixed_to_int! { (FixedU64, FixedI64) -> wider (u128, i128) }
fixed_to_int! { (FixedU128, FixedI128) -> (u128, i128) }
macro_rules! fixed_to_int_lossy {
(($SrcU:ident, $SrcI:ident, $SrcBits:ident) -> ($DstU:ident, $DstI:ident, $DstBits:ident)) => {
// Conditions: $SrcBits <= FracSrc + $DstBits
impl<FracSrc, FracSrcP> LossyFrom<$SrcU<FracSrc>> for $DstU
where
FracSrc: Unsigned
+ IsLessOrEqual<$SrcBits, Output = True>
+ Add<$DstBits, Output = FracSrcP>,
FracSrcP: Unsigned,
$SrcBits: IsLessOrEqual<FracSrcP, Output = True>,
{
#[inline]
fn lossy_from(src: $SrcU<FracSrc>) -> $DstU {
FromFixed::wrapping_from_fixed(src)
}
}
// Conditions: $SrcBits <= FracSrc + $DstBits
impl<FracSrc, FracSrcP> LossyFrom<$SrcI<FracSrc>> for $DstI
where
FracSrc: Unsigned
+ IsLessOrEqual<$SrcBits, Output = True>
+ Add<$DstBits, Output = FracSrcP>,
FracSrcP: Unsigned,
$SrcBits: IsLessOrEqual<FracSrcP, Output = True>,
{
#[inline]
fn lossy_from(src: $SrcI<FracSrc>) -> $DstI {
FromFixed::wrapping_from_fixed(src)
}
}
// Conditions: $SrcBits + 1 <= FracSrc + $DstBits
impl<FracSrc, FracSrcP> LossyFrom<$SrcU<FracSrc>> for $DstI
where
FracSrc: Unsigned
+ IsLessOrEqual<$SrcBits, Output = True>
+ Add<$DstBits, Output = FracSrcP>,
FracSrcP: Unsigned,
<$SrcBits as Add<U1>>::Output: IsLessOrEqual<FracSrcP, Output = True>,
{
#[inline]
fn lossy_from(src: $SrcU<FracSrc>) -> $DstI {
FromFixed::wrapping_from_fixed(src)
}
}
};
($SrcU:ident, $SrcI:ident, $SrcBits:ident) => {
fixed_to_int_lossy! { ($SrcU, $SrcI, $SrcBits) -> (u8, i8, U8) }
fixed_to_int_lossy! { ($SrcU, $SrcI, $SrcBits) -> (u16, i16, U16) }
fixed_to_int_lossy! { ($SrcU, $SrcI, $SrcBits) -> (u32, i32, U32) }
fixed_to_int_lossy! { ($SrcU, $SrcI, $SrcBits) -> (u64, i64, U64) }
fixed_to_int_lossy! { ($SrcU, $SrcI, $SrcBits) -> (u128, i128, U128) }
};
}
fixed_to_int_lossy! { FixedU8, FixedI8, U8 }
fixed_to_int_lossy! { FixedU16, FixedI16, U16 }
fixed_to_int_lossy! { FixedU32, FixedI32, U32 }
fixed_to_int_lossy! { FixedU64, FixedI64, U64 }
fixed_to_int_lossy! { FixedU128, FixedI128, U128 }
macro_rules! fixed_to_float {
($Fixed:ident($Len:ty) -> $Float:ident) => {
impl<Frac> From<$Fixed<Frac>> for $Float
where
Frac: Unsigned + IsLessOrEqual<$Len, Output = True>,
{
#[inline]
fn from(src: $Fixed<Frac>) -> $Float {
src.to_float()
}
}
};
}
#[cfg(feature = "f16")]
fixed_to_float! { FixedI8(U8) -> f16 }
#[cfg(feature = "f16")]
fixed_to_float! { FixedU8(U8) -> f16 }
fixed_to_float! { FixedI8(U8) -> f32 }
fixed_to_float! { FixedI16(U16) -> f32 }
fixed_to_float! { FixedU8(U8) -> f32 }
fixed_to_float! { FixedU16(U16) -> f32 }
fixed_to_float! { FixedI8(U8) -> f64 }
fixed_to_float! { FixedI16(U16) -> f64 }
fixed_to_float! { FixedI32(U32) -> f64 }
fixed_to_float! { FixedU8(U8) -> f64 }
fixed_to_float! { FixedU16(U16) -> f64 }
fixed_to_float! { FixedU32(U32) -> f64 }
macro_rules! fixed_to_float_lossy {
($Fixed:ident($Len:ty) -> $Float:ident) => {
impl<Frac> LossyFrom<$Fixed<Frac>> for $Float
where
Frac: Unsigned + IsLessOrEqual<$Len, Output = True>,
{
#[inline]
fn lossy_from(src: $Fixed<Frac>) -> $Float {
src.to_float()
}
}
};
($Fixed:ident($Len:ty)) => {
#[cfg(feature = "f16")]
fixed_to_float_lossy! { $Fixed($Len) -> f16 }
fixed_to_float_lossy! { $Fixed($Len) -> f32 }
fixed_to_float_lossy! { $Fixed($Len) -> f64 }
};
}
fixed_to_float_lossy! { FixedI8(U8) }
fixed_to_float_lossy! { FixedI16(U16) }
fixed_to_float_lossy! { FixedI32(U32) }
fixed_to_float_lossy! { FixedI64(U64) }
fixed_to_float_lossy! { FixedI128(U128) }
fixed_to_float_lossy! { FixedU8(U8) }
fixed_to_float_lossy! { FixedU16(U16) }
fixed_to_float_lossy! { FixedU32(U32) }
fixed_to_float_lossy! { FixedU64(U64) }
fixed_to_float_lossy! { FixedU128(U128) }
macro_rules! int_to_float_lossy {
($Int:ident -> $Float:ident) => {
impl LossyFrom<$Int> for $Float {
#[inline]
fn lossy_from(src: $Int) -> $Float {
<$Int as SealedInt>::to_repr_fixed(src).to_float()
}
}
};
($Int:ident) => {
#[cfg(feature = "f16")]
int_to_float_lossy! { $Int -> f16 }
int_to_float_lossy! { $Int -> f32 }
int_to_float_lossy! { $Int -> f64 }
};
}
int_to_float_lossy! { i8 }
int_to_float_lossy! { i16 }
int_to_float_lossy! { i32 }
int_to_float_lossy! { i64 }
int_to_float_lossy! { i128 }
int_to_float_lossy! { isize }
int_to_float_lossy! { u8 }
int_to_float_lossy! { u16 }
int_to_float_lossy! { u32 }
int_to_float_lossy! { u64 }
int_to_float_lossy! { u128 }
int_to_float_lossy! { usize }
#[cfg(feature = "f16")]
impl LossyFrom<f16> for f16 {
#[inline]
fn lossy_from(src: f16) -> f16 {
src
}
}
#[cfg(feature = "f16")]
impl LossyFrom<f16> for f32 {
#[inline]
fn lossy_from(src: f16) -> f32 {
f32::from(src)
}
}
#[cfg(feature = "f16")]
impl LossyFrom<f16> for f64 {
#[inline]
fn lossy_from(src: f16) -> f64 {
f64::from(src)
}
}
#[cfg(feature = "f16")]
impl LossyFrom<f32> for f16 {
#[inline]
fn lossy_from(src: f32) -> f16 {
f16::from_f32(src)
}
}
impl LossyFrom<f32> for f32 {
#[inline]
fn lossy_from(src: f32) -> f32 {
src
}
}
impl LossyFrom<f32> for f64 {
#[inline]
fn lossy_from(src: f32) -> f64 {
f64::from(src)
}
}
#[cfg(feature = "f16")]
impl LossyFrom<f64> for f16 {
#[inline]
fn lossy_from(src: f64) -> f16 {
f16::from_f64(src)
}
}
impl LossyFrom<f64> for f32 {
#[inline]
fn lossy_from(src: f64) -> f32 {
src as f32
}
}
impl LossyFrom<f64> for f64 {
#[inline]
fn lossy_from(src: f64) -> f64 {
src
}
}
#[cfg_attr(feature = "cargo-clippy", allow(clippy::float_cmp))]
#[cfg(test)]
mod tests {
use crate::*;
#[test]
fn expanding_from_unsigned() {
type L8 = FixedU8<frac::U0>;
type LL16 = FixedU16<frac::U0>;
type LH16 = FixedU16<frac::U8>;
type LL128 = FixedU128<frac::U0>;
type LH128 = FixedU128<frac::U120>;
type H8 = FixedU8<frac::U8>;
type HL16 = FixedU16<frac::U8>;
type HH16 = FixedU16<frac::U16>;
type HL128 = FixedU128<frac::U8>;
type HH128 = FixedU128<frac::U128>;
let vals: &[u8] = &[0x00, 0x7f, 0x80, 0xff];
for &val in vals {
let val16 = u16::from(val);
let val128 = u128::from(val);
let l = L8::from_bits(val);
assert_eq!(l, L8::from(val));
assert_eq!(val, u8::from(l));
assert_eq!(LL16::from(l), LL16::from_bits(val16));
assert_eq!(LH16::from(l), LH16::from_bits(val16 << 8));
assert_eq!(LL128::from(l), LL128::from_bits(val128));
assert_eq!(LH128::from(l), LH128::from_bits(val128 << 120));
let h = H8::from_bits(val);
assert_eq!(HL16::from(h), HL16::from_bits(val16));
assert_eq!(HH16::from(h), HH16::from_bits(val16 << 8));
assert_eq!(HL128::from(h), HL128::from_bits(val128));
assert_eq!(HH128::from(h), HH128::from_bits(val128 << 120));
}
}
#[test]
fn expanding_from_signed() {
type L8 = FixedI8<frac::U0>;
type LL16 = FixedI16<frac::U0>;
type LH16 = FixedI16<frac::U8>;
type LL128 = FixedI128<frac::U0>;
type LH128 = FixedI128<frac::U120>;
type H8 = FixedI8<frac::U8>;
type HL16 = FixedI16<frac::U8>;
type HH16 = FixedI16<frac::U16>;
type HL128 = FixedI128<frac::U8>;
type HH128 = FixedI128<frac::U128>;
let vals: &[i8] = &[0x00, 0x7f, -0x80, -0x01];
for &val in vals {
let val16 = i16::from(val);
let val128 = i128::from(val);
let l = L8::from_bits(val);
assert_eq!(l, L8::from(val));
assert_eq!(val, i8::from(l));
assert_eq!(LL16::from(l), LL16::from_bits(val16));
assert_eq!(LH16::from(l), LH16::from_bits(val16 << 8));
assert_eq!(LL128::from(l), LL128::from_bits(val128));
assert_eq!(LH128::from(l), LH128::from_bits(val128 << 120));
let h = H8::from_bits(val);
assert_eq!(HL16::from(h), HL16::from_bits(val16));
assert_eq!(HH16::from(h), HH16::from_bits(val16 << 8));
assert_eq!(HL128::from(h), HL128::from_bits(val128));
assert_eq!(HH128::from(h), HH128::from_bits(val128 << 120));
}
}
#[test]
fn expanding_from_unsigned_to_signed() {
type L8 = FixedU8<frac::U0>;
type LL16 = FixedI16<frac::U0>;
type LH16 = FixedI16<frac::U7>;
type LL128 = FixedI128<frac::U0>;
type LH128 = FixedI128<frac::U119>;
type H8 = FixedU8<frac::U8>;
type HL16 = FixedI16<frac::U8>;
type HH16 = FixedI16<frac::U15>;
type HL128 = FixedI128<frac::U8>;
type HH128 = FixedI128<frac::U127>;
let vals: &[u8] = &[0x00, 0x7f, 0x80, 0xff];
for &val in vals {
let val16 = i16::from(val);
let val128 = i128::from(val);
let l = L8::from_bits(val);
assert_eq!(l, L8::from(val));
assert_eq!(val, u8::from(l));
assert_eq!(LL16::from(l), LL16::from_bits(val16));
assert_eq!(LH16::from(l), LH16::from_bits(val16 << 7));
assert_eq!(LL128::from(l), LL128::from_bits(val128));
assert_eq!(LH128::from(l), LH128::from_bits(val128 << 119));
let h = H8::from_bits(val);
assert_eq!(HL16::from(h), HL16::from_bits(val16));
assert_eq!(HH16::from(h), HH16::from_bits(val16 << 7));
assert_eq!(HL128::from(h), HL128::from_bits(val128));
assert_eq!(HH128::from(h), HH128::from_bits(val128 << 119));
}
}
#[test]
fn from_bool() {
assert_eq!(FixedI8::<frac::U6>::from(true), 1);
assert_eq!(FixedI8::<frac::U6>::from(false), 0);
assert_eq!(FixedI128::<frac::U64>::from(true), 1);
assert_eq!(FixedU128::<frac::U127>::from(true), 1);
}
#[test]
fn to_size() {
let min_i24 = FixedI32::<frac::U8>::min_value();
let max_i24 = FixedI32::<frac::U8>::max_value();
let max_u24 = FixedU32::<frac::U8>::max_value();
assert_eq!(min_i24.overflowing_to_int::<isize>(), (!0 << 23, false));
assert_eq!(max_i24.overflowing_to_int::<isize>(), (!(!0 << 23), false));
assert_eq!(max_u24.overflowing_to_int::<isize>(), (!(!0 << 24), false));
assert_eq!(min_i24.overflowing_to_int::<usize>(), (!0 << 23, true));
assert_eq!(max_i24.overflowing_to_int::<usize>(), (!(!0 << 23), false));
assert_eq!(max_u24.overflowing_to_int::<usize>(), (!(!0 << 24), false));
let min_i56 = FixedI64::<frac::U8>::min_value();
let max_i56 = FixedI64::<frac::U8>::max_value();
let max_u56 = FixedU64::<frac::U8>::max_value();
#[cfg(target_pointer_width = "32")]
{
assert_eq!(min_i56.overflowing_to_int::<isize>(), (0, true));
assert_eq!(max_i56.overflowing_to_int::<isize>(), (!0, true));
assert_eq!(max_u56.overflowing_to_int::<isize>(), (!0, true));
assert_eq!(min_i56.overflowing_to_int::<usize>(), (0, true));
assert_eq!(max_i56.overflowing_to_int::<usize>(), (!0, true));
assert_eq!(max_u56.overflowing_to_int::<usize>(), (!0, true));
}
#[cfg(target_pointer_width = "64")]
{
assert_eq!(min_i56.overflowing_to_int::<isize>(), (!0 << 55, false));
assert_eq!(max_i56.overflowing_to_int::<isize>(), (!(!0 << 55), false));
assert_eq!(max_u56.overflowing_to_int::<isize>(), (!(!0 << 56), false));
assert_eq!(min_i56.overflowing_to_int::<usize>(), (!0 << 55, true));
assert_eq!(max_i56.overflowing_to_int::<usize>(), (!(!0 << 55), false));
assert_eq!(max_u56.overflowing_to_int::<usize>(), (!(!0 << 56), false));
}
let min_i120 = FixedI128::<frac::U8>::min_value();
let max_i120 = FixedI128::<frac::U8>::max_value();
let max_u120 = FixedU128::<frac::U8>::max_value();
assert_eq!(min_i120.overflowing_to_int::<isize>(), (0, true));
assert_eq!(max_i120.overflowing_to_int::<isize>(), (!0, true));
assert_eq!(max_u120.overflowing_to_int::<isize>(), (!0, true));
assert_eq!(min_i120.overflowing_to_int::<usize>(), (0, true));
assert_eq!(max_i120.overflowing_to_int::<usize>(), (!0, true));
assert_eq!(max_u120.overflowing_to_int::<usize>(), (!0, true));
}
#[test]
fn signed_from_float() {
type Fix = FixedI8<frac::U4>;
// 1.1 -> 0001.1000
assert_eq!(Fix::from_float(3.0 / 2.0), Fix::from_bits(24));
// 0.11 -> 0000.1100
assert_eq!(Fix::from_float(3.0 / 4.0), Fix::from_bits(12));
// 0.011 -> 0000.0110
assert_eq!(Fix::from_float(3.0 / 8.0), Fix::from_bits(6));
// 0.0011 -> 0000.0011
assert_eq!(Fix::from_float(3.0 / 16.0), Fix::from_bits(3));
// 0.00011 -> 0000.0010 (tie to even)
assert_eq!(Fix::from_float(3.0 / 32.0), Fix::from_bits(2));
// 0.00101 -> 0000.0010 (tie to even)
assert_eq!(Fix::from_float(5.0 / 32.0), Fix::from_bits(2));
// 0.000011 -> 0000.0001 (nearest)
assert_eq!(Fix::from_float(3.0 / 64.0), Fix::from_bits(1));
// 0.00001 -> 0000.0000 (tie to even)
assert_eq!(Fix::from_float(1.0 / 32.0), Fix::from_bits(0));
// -1.1 -> -0001.1000
assert_eq!(Fix::from_float(-3.0 / 2.0), Fix::from_bits(-24));
// -0.11 -> -0000.1100
assert_eq!(Fix::from_float(-3.0 / 4.0), Fix::from_bits(-12));
// -0.011 -> -0000.0110
assert_eq!(Fix::from_float(-3.0 / 8.0), Fix::from_bits(-6));
// -0.0011 -> -0000.0011
assert_eq!(Fix::from_float(-3.0 / 16.0), Fix::from_bits(-3));
// -0.00011 -> -0000.0010 (tie to even)
assert_eq!(Fix::from_float(-3.0 / 32.0), Fix::from_bits(-2));
// -0.00101 -> -0000.0010 (tie to even)
assert_eq!(Fix::from_float(-5.0 / 32.0), Fix::from_bits(-2));
// -0.000011 -> -0000.0001 (nearest)
assert_eq!(Fix::from_float(-3.0 / 64.0), Fix::from_bits(-1));
// -0.00001 -> 0000.0000 (tie to even)
assert_eq!(Fix::from_float(-1.0 / 32.0), Fix::from_bits(0));
// 111.1111 -> 111.1111
assert_eq!(Fix::from_float(127.0 / 16.0), Fix::from_bits(127));
// 111.11111 -> 1000.0000, too large (tie to even)
assert_eq!(
Fix::overflowing_from_float(255.0 / 32.0),
(Fix::from_bits(-128), true)
);
// -111.1111 -> -111.1111
assert_eq!(Fix::from_float(-127.0 / 16.0), Fix::from_bits(-127));
// -111.11111 -> -1000.0000 (tie to even)
assert_eq!(Fix::from_float(-255.0 / 32.0), Fix::from_bits(-128));
// -1000.00001 -> -1000.0000 (tie to even)
assert_eq!(Fix::from_float(-257.0 / 32.0), Fix::from_bits(-128));
// -1000.0001 -> too small
assert_eq!(
Fix::overflowing_from_float(-129.0 / 16.0),
(Fix::from_bits(127), true)
);
}
#[test]
fn unsigned_from_float() {
type Fix = FixedU8<frac::U4>;
// 1.1 -> 0001.1000
assert_eq!(Fix::from_float(3.0 / 2.0), Fix::from_bits(24));
// 0.11 -> 0000.1100
assert_eq!(Fix::from_float(3.0 / 4.0), Fix::from_bits(12));
// 0.011 -> 0000.0110
assert_eq!(Fix::from_float(3.0 / 8.0), Fix::from_bits(6));
// 0.0011 -> 0000.0011
assert_eq!(Fix::from_float(3.0 / 16.0), Fix::from_bits(3));
// 0.00011 -> 0000.0010 (tie to even)
assert_eq!(Fix::from_float(3.0 / 32.0), Fix::from_bits(2));
// 0.00101 -> 0000.0010 (tie to even)
assert_eq!(Fix::from_float(5.0 / 32.0), Fix::from_bits(2));
// 0.000011 -> 0000.0001 (nearest)
assert_eq!(Fix::from_float(3.0 / 64.0), Fix::from_bits(1));
// 0.00001 -> 0000.0000 (tie to even)
assert_eq!(Fix::from_float(1.0 / 32.0), Fix::from_bits(0));
// -0.00001 -> 0000.0000 (tie to even)
assert_eq!(Fix::from_float(-1.0 / 32.0), Fix::from_bits(0));
// -0.0001 -> too small
assert_eq!(
Fix::overflowing_from_float(-1.0 / 16.0),
(Fix::from_bits(255), true)
);
// 1111.1111 -> 1111.1111
assert_eq!(Fix::from_float(255.0 / 16.0), Fix::from_bits(255));
// 1111.11111 -> too large (tie to even)
assert_eq!(
Fix::overflowing_from_float(511.0 / 32.0),
(Fix::from_bits(0), true)
);
}
#[cfg(feature = "f16")]
#[test]
fn to_f16() {
use half::f16;
for u in 0x00..=0xff {
let fu = FixedU8::<frac::U7>::from_bits(u);
assert_eq!(fu.to_float::<f16>(), f16::from_f32(f32::from(u) / 128.0));
let i = u as i8;
let fi = FixedI8::<frac::U7>::from_bits(i);
assert_eq!(fi.to_float::<f16>(), f16::from_f32(f32::from(i) / 128.0));
for hi in &[
0u32,
0x0000_0100,
0x7fff_ff00,
0x8000_0000,
0x8100_0000,
0xffff_fe00,
0xffff_ff00,
] {
let uu = *hi | u32::from(u);
let fuu = FixedU32::<frac::U7>::from_bits(uu);
assert_eq!(fuu.to_float::<f16>(), f16::from_f32(uu as f32 / 128.0));
let ii = uu as i32;
let fii = FixedI32::<frac::U7>::from_bits(ii);
assert_eq!(fii.to_float::<f16>(), f16::from_f32(ii as f32 / 128.0));
}
for hi in &[
0u128,
0x0000_0000_0000_0000_0000_0000_0000_0100,
0x7fff_ffff_ffff_ffff_ffff_ffff_ffff_ff00,
0x8000_0000_0000_0000_0000_0000_0000_0000,
0x8100_0000_0000_0000_0000_0000_0000_0000,
0xffff_ffff_ffff_ffff_ffff_ffff_ffff_fe00,
0xffff_ffff_ffff_ffff_ffff_ffff_ffff_ff00,
] {
let uu = *hi | u128::from(u);
let fuu = FixedU128::<frac::U7>::from_bits(uu);
assert_eq!(fuu.to_float::<f16>(), f16::from_f64(uu as f64 / 128.0));
let ii = uu as i128;
let fii = FixedI128::<frac::U7>::from_bits(ii);
assert_eq!(fii.to_float::<f16>(), f16::from_f64(ii as f64 / 128.0));
}
}
}
#[test]
fn to_f32() {
for u in 0x00..=0xff {
let fu = FixedU8::<frac::U7>::from_bits(u);
assert_eq!(fu.to_float::<f32>(), f32::from(u) / 128.0);
let i = u as i8;
let fi = FixedI8::<frac::U7>::from_bits(i);
assert_eq!(fi.to_float::<f32>(), f32::from(i) / 128.0);
for hi in &[
0u32,
0x0000_0100,
0x7fff_ff00,
0x8000_0000,
0x8100_0000,
0xffff_fe00,
0xffff_ff00,
] {
let uu = *hi | u32::from(u);
let fuu = FixedU32::<frac::U7>::from_bits(uu);
assert_eq!(fuu.to_float::<f32>(), uu as f32 / 128.0);
let ii = uu as i32;
let fii = FixedI32::<frac::U7>::from_bits(ii);
assert_eq!(fii.to_float::<f32>(), ii as f32 / 128.0);
}
for hi in &[
0u128,
0x0000_0000_0000_0000_0000_0000_0000_0100,
0x7fff_ffff_ffff_ffff_ffff_ffff_ffff_ff00,
0x8000_0000_0000_0000_0000_0000_0000_0000,
0x8100_0000_0000_0000_0000_0000_0000_0000,
0xffff_ffff_ffff_ffff_ffff_ffff_ffff_fe00,
0xffff_ffff_ffff_ffff_ffff_ffff_ffff_ff00,
] {
let uu = *hi | u128::from(u);
let fuu = FixedU128::<frac::U7>::from_bits(uu);
assert_eq!(fuu.to_float::<f32>(), (uu as f64 / 128.0) as f32);
let ii = uu as i128;
let fii = FixedI128::<frac::U7>::from_bits(ii);
assert_eq!(fii.to_float::<f32>(), (ii as f64 / 128.0) as f32);
}
}
}
#[test]
fn to_infinite_f32() {
// too_large is 1.ffff_ffff_ffff... << 127,
// which will be rounded to 1.0 << 128.
let too_large = FixedU128::<frac::U0>::max_value();
assert_eq!(too_large.count_ones(), 128);
assert!(too_large.to_float::<f32>().is_infinite());
// still_too_large is 1.ffff_ff << 127,
// which is exactly midway between 1.0 << 128 (even)
// and the largest normal f32 that is 1.ffff_fe << 127 (odd).
// The tie will be rounded to even, which is to 1.0 << 128.
let still_too_large = too_large << 103u32;
assert_eq!(still_too_large.count_ones(), 25);
assert!(still_too_large.to_float::<f32>().is_infinite());
// not_too_large is 1.ffff_feff_ffff... << 127,
// which will be rounded to 1.ffff_fe << 127.
let not_too_large = still_too_large - FixedU128::from_bits(1);
assert_eq!(not_too_large.count_ones(), 127);
assert!(!not_too_large.to_float::<f32>().is_infinite());
// min_128 is -1.0 << 127.
let min_i128 = FixedI128::<frac::U0>::min_value();
assert_eq!(min_i128.count_ones(), 1);
assert_eq!(min_i128.to_float::<f32>(), -(127f32.exp2()));
}
#[test]
fn to_f64() {
for u in 0x00..=0xff {
let fu = FixedU8::<frac::U7>::from_bits(u);
assert_eq!(fu.to_float::<f64>(), f64::from(u) / 128.0);
let i = u as i8;
let fi = FixedI8::<frac::U7>::from_bits(i);
assert_eq!(fi.to_float::<f64>(), f64::from(i) / 128.0);
for hi in &[
0u64,
0x0000_0000_0000_0100,
0x7fff_ffff_ffff_ff00,
0x8000_0000_0000_0000,
0x8100_0000_0000_0000,
0xffff_ffff_ffff_fe00,
0xffff_ffff_ffff_ff00,
] {
let uu = *hi | u64::from(u);
let fuu = FixedU64::<frac::U7>::from_bits(uu);
assert_eq!(fuu.to_float::<f64>(), uu as f64 / 128.0);
let ii = uu as i64;
let fii = FixedI64::<frac::U7>::from_bits(ii);
assert_eq!(fii.to_float::<f64>(), ii as f64 / 128.0);
}
for hi in &[
0u128,
0x0000_0000_0000_0000_0000_0000_0000_0100,
0x7fff_ffff_ffff_ffff_ffff_ffff_ffff_ff00,
0x8000_0000_0000_0000_0000_0000_0000_0000,
0x8100_0000_0000_0000_0000_0000_0000_0000,
0xffff_ffff_ffff_ffff_ffff_ffff_ffff_fe00,
0xffff_ffff_ffff_ffff_ffff_ffff_ffff_ff00,
] {
let uu = *hi | u128::from(u);
let fuu = FixedU128::<frac::U7>::from_bits(uu);
assert_eq!(fuu.to_float::<f64>(), uu as f64 / 128.0);
let ii = uu as i128;
let fii = FixedI128::<frac::U7>::from_bits(ii);
assert_eq!(fii.to_float::<f64>(), ii as f64 / 128.0);
}
}
}
}