use crate::*;
macro_rules! num_impl {
($Name:ident, $T:ty) => {
impl Num for $T {
type Raw = $T;
type Output<const B: u32, const S: i32> = $Name<B, S>;
const BITS: u32 = <$T>::BITS;
const SHIFT: i32 = 0;
const MIN: $T = <$T>::MIN;
const MAX: $T = <$T>::MAX;
#[allow(unused_comparisons)]
const SIGNED: bool = <$T>::MIN < 0;
unsafe fn new_unchecked(val: $T) -> Self {
val
}
unsafe fn from_f32_unchecked(val: f32) -> Self {
val.to_int_unchecked()
}
unsafe fn from_f64_unchecked(val: f64) -> Self {
val.to_int_unchecked()
}
fn raw(self) -> $T {
self
}
fn into_f32(self) -> f32 {
assert!(
Self::BITS <= f32::MANTISSA_DIGITS,
"number could be truncated in f32"
);
self as f32
}
fn into_f64(self) -> f64 {
assert!(
Self::BITS <= f32::MANTISSA_DIGITS,
"number could be truncated in f64"
);
self as f64
}
}
#[repr(transparent)]
#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
#[doc = concat!("[`", stringify!($T), "`]")]
pub struct $Name<const BITS: u32, const SHIFT: i32>($T);
impl<const BITS: u32, const SHIFT: i32> Num for $Name<BITS, SHIFT> {
type Raw = $T;
type Output<const B: u32, const S: i32> = $Name<B, S>;
const BITS: u32 = {
assert!(BITS <= <$T>::BITS, concat!("too many bits for ", stringify!($T)));
BITS
};
const SHIFT: i32 = SHIFT;
const MIN: Self = Self({
if Self::BITS == 0 {
0
} else {
<$T>::MIN >> (<$T>::BITS - Self::BITS)
}
});
const MAX: Self = Self({
if Self::BITS == 0 {
0
} else {
<$T>::MAX >> (<$T>::BITS - Self::BITS)
}
});
const SIGNED: bool = <$T>::SIGNED;
unsafe fn new_unchecked(val: $T) -> Self {
let _ = Self::BITS; Self(val)
}
unsafe fn from_f32_unchecked(val: f32) -> Self {
unsafe { Self::new_unchecked((val / f32_lsb::<SHIFT>()).to_int_unchecked()) }
}
unsafe fn from_f64_unchecked(val: f64) -> Self {
unsafe { Self::new_unchecked((val / f64_lsb::<SHIFT>()).to_int_unchecked()) }
}
fn raw(self) -> $T {
self.0
}
fn into_f32(self) -> f32 {
assert!(
BITS <= f32::MANTISSA_DIGITS,
"number could be truncated in f32"
);
assert!(
SHIFT <= f32::MANTISSA_DIGITS as i32 - f32::MIN_EXP,
"number could underflow f32"
);
assert!(
BITS as i32 - SHIFT <= f32::MAX_EXP as i32,
"number could overflow f32"
);
if BITS == 0 { 0. } else { self.0 as f32 * f32_lsb::<SHIFT>() }
}
fn into_f64(self) -> f64 {
assert!(
BITS <= f64::MANTISSA_DIGITS,
"number could be truncated in f64"
);
assert!(
SHIFT <= f64::MANTISSA_DIGITS as i32 - f64::MIN_EXP,
"number could underflow f64"
);
assert!(
BITS as i32 - SHIFT <= f64::MAX_EXP as i32,
"number could overflow f64"
);
if BITS == 0 { 0. } else { self.0 as f64 * f64_lsb::<SHIFT>() }
}
}
impl<const BITS: u32, const SHIFT: i32> From<$Name<BITS, SHIFT>> for f32 {
fn from(val: $Name<BITS, SHIFT>) -> f32 {
val.into_f32()
}
}
impl<const BITS: u32, const SHIFT: i32> From<$Name<BITS, SHIFT>> for f64 {
fn from(val: $Name<BITS, SHIFT>) -> f64 {
val.into_f64()
}
}
impl<const BITS: u32, const SHIFT: i32> TryFrom<f32> for $Name<BITS, SHIFT> {
type Error = RangeError;
fn try_from(val: f32) -> Result<Self, Self::Error> {
Self::from_f32(val)
}
}
impl<const BITS: u32, const SHIFT: i32> TryFrom<f64> for $Name<BITS, SHIFT> {
type Error = RangeError;
fn try_from(val: f64) -> Result<Self, Self::Error> {
Self::from_f64(val)
}
}
#[doc = concat!("`", stringify!($T), "` is the same as `", stringify!($Name), "<", stringify!($T) ,"::BITS, 0>`.")]
impl From<$T> for $Name<{ <$T>::BITS }, 0> {
fn from(val: $T) -> Self {
unsafe { Self::new_unchecked(val) }
}
}
#[doc = concat!("`", stringify!($T), "` is the same as `", stringify!($Name), "<", stringify!($T) ,"::BITS, 0>`.")]
impl From<$Name<{ <$T>::BITS }, 0>> for $T {
fn from(val: $Name<{ <$T>::BITS }, 0>) -> Self {
val.raw()
}
}
};
}
num_impl!(I8, i8);
num_impl!(U8, u8);
num_impl!(I16, i16);
num_impl!(U16, u16);
num_impl!(I32, i32);
num_impl!(U32, u32);
num_impl!(I64, i64);
num_impl!(U64, u64);
num_impl!(I128, i128);
num_impl!(U128, u128);
num_impl!(Isize, isize);
num_impl!(Usize, usize);
macro_rules! num_signed_unsigned_impl {
($Uname:ident, $Iname:ident) => {
impl<const B: u32, const S: i32> $Uname<B, S> {
pub fn into_signed(self) -> $Iname<{ B + 1 }, S>
where
[(); (B + 1) as usize]:,
{
unsafe { $Iname::new_unchecked(self.raw() as <$Iname<{ B + 1 }, S> as Num>::Raw) }
}
}
impl<const B: u32, const S: i32> $Iname<B, S> {
pub unsafe fn into_unsigned_unchecked(self) -> $Uname<{ B - 1 }, S>
where
[(); (B - 1) as usize]:,
{
unsafe { $Uname::new_unchecked(self.raw() as <$Uname<B, S> as Num>::Raw) }
}
pub fn into_unsigned(self) -> Option<$Uname<{ B - 1 }, S>>
where
[(); (B - 1) as usize]:,
{
if self.raw() >= 0 {
Some(unsafe { self.into_unsigned_unchecked() })
} else {
None
}
}
}
};
}
num_signed_unsigned_impl!(U8, I8);
num_signed_unsigned_impl!(U16, I16);
num_signed_unsigned_impl!(U32, I32);
num_signed_unsigned_impl!(U64, I64);
num_signed_unsigned_impl!(U128, I128);
num_signed_unsigned_impl!(Usize, Isize);
fn f32_lsb<const SHIFT: i32>() -> f32 {
let exp = 2 - f32::MIN_EXP - SHIFT;
f32::from_bits(if exp > 0 {
(exp as u32) << (f32::MANTISSA_DIGITS - 1)
} else {
1u32 << (f32::MANTISSA_DIGITS as i32 + exp - 2)
})
}
fn f64_lsb<const SHIFT: i32>() -> f64 {
let exp = 2 - f64::MIN_EXP - SHIFT;
f64::from_bits(if exp > 0 {
(exp as u64) << (f64::MANTISSA_DIGITS - 1)
} else {
1u64 << (f64::MANTISSA_DIGITS as i32 + exp - 2)
})
}