use crate::ext::xmpfr;
use crate::float::small;
use crate::float::{Round, SmallFloat};
#[cfg(feature = "integer")]
use crate::Integer;
#[cfg(feature = "rational")]
use crate::Rational;
use crate::{Assign, Float};
use az::{Cast, CheckedAs, CheckedCast, SaturatingCast, UnwrappedCast, WrappingAs};
use core::cmp::Ordering;
macro_rules! cast_int_uint_common {
($Prim:ty) => {
impl Cast<$Prim> for Float {
#[inline]
fn cast(self) -> $Prim {
(&self).cast()
}
}
impl Cast<$Prim> for &'_ Float {
#[inline]
fn cast(self) -> $Prim {
self.unwrapped_cast()
}
}
impl CheckedCast<$Prim> for Float {
#[inline]
fn checked_cast(self) -> Option<$Prim> {
(&self).checked_cast()
}
}
impl SaturatingCast<$Prim> for Float {
#[inline]
fn saturating_cast(self) -> $Prim {
(&self).saturating_cast()
}
}
impl SaturatingCast<$Prim> for &'_ Float {
fn saturating_cast(self) -> $Prim {
if self.is_nan() {
panic!("NaN");
}
match self.checked_cast() {
Some(val) => val,
None => {
if self.is_sign_negative() {
<$Prim>::MIN
} else {
<$Prim>::MAX
}
}
}
}
}
impl UnwrappedCast<$Prim> for Float {
#[inline]
fn unwrapped_cast(self) -> $Prim {
(&self).unwrapped_cast()
}
}
impl UnwrappedCast<$Prim> for &'_ Float {
#[inline]
fn unwrapped_cast(self) -> $Prim {
if self.is_nan() {
panic!("NaN");
}
self.checked_cast().expect("overflow")
}
}
};
}
macro_rules! cast_int {
($Prim:ty, $U:ty, $nbits:expr, $unchecked_get:path) => {
cast_int_uint_common! { $Prim }
impl CheckedCast<$Prim> for &'_ Float {
fn checked_cast(self) -> Option<$Prim> {
if !self.is_finite() {
return None;
}
const ZERO: $Prim = 0;
let mut small = SmallFloat::from(ZERO);
unsafe {
small
.as_nonreallocating_float()
.assign(self.round_even_ref());
}
debug_assert!(!small.is_nan());
let cmp0 = xmpfr::sgn_not_nan(&*small);
match cmp0 {
Ordering::Less => match small.get_exp() {
None => None,
Some(exp) if exp > $nbits => None,
Some(exp) => {
debug_assert!(small.is_normal());
let abs = unsafe { $unchecked_get(&small) >> ($nbits - exp) };
if abs > <$Prim>::MIN.wrapping_as::<$U>() {
None
} else {
Some(abs.wrapping_as::<$Prim>().wrapping_neg())
}
}
},
Ordering::Equal => Some(0),
Ordering::Greater => match small.get_exp() {
None => None,
Some(exp) if exp >= $nbits => None,
Some(exp) => {
debug_assert!(small.is_normal());
let abs = unsafe { $unchecked_get(&small) >> ($nbits - exp) };
Some(abs.wrapping_as::<$Prim>())
}
},
}
}
}
};
}
macro_rules! cast_uint {
($Prim:ty, $nbits:expr, $unchecked_get:path) => {
cast_int_uint_common! { $Prim }
impl CheckedCast<$Prim> for &'_ Float {
fn checked_cast(self) -> Option<$Prim> {
if !self.is_finite() {
return None;
}
const ZERO: $Prim = 0;
let mut small = SmallFloat::from(ZERO);
unsafe {
small
.as_nonreallocating_float()
.assign(self.round_even_ref());
}
debug_assert!(!small.is_nan());
let cmp0 = xmpfr::sgn_not_nan(&*small);
match cmp0 {
Ordering::Less => None,
Ordering::Equal => Some(0),
Ordering::Greater => match small.get_exp() {
None => None,
Some(exp) if exp > $nbits => None,
Some(exp) => {
debug_assert!(small.is_normal());
Some(unsafe { $unchecked_get(&small) >> ($nbits - exp) })
}
},
}
}
}
};
}
cast_int! { i8, u8, 8, small::unchecked_get_unshifted_u8 }
cast_int! { i16, u16, 16, small::unchecked_get_unshifted_u16 }
cast_int! { i32, u32, 32, small::unchecked_get_unshifted_u32 }
cast_int! { i64, u64, 64, small::unchecked_get_unshifted_u64 }
cast_int! { i128, u128, 128, small::unchecked_get_unshifted_u128 }
cast_int_uint_common! { isize }
impl CheckedCast<isize> for &'_ Float {
#[inline]
fn checked_cast(self) -> Option<isize> {
#[cfg(target_pointer_width = "32")]
{
self.checked_as::<i32>().map(az::cast)
}
#[cfg(target_pointer_width = "64")]
{
self.checked_as::<i64>().map(az::cast)
}
}
}
cast_uint! { u8, 8, small::unchecked_get_unshifted_u8 }
cast_uint! { u16, 16, small::unchecked_get_unshifted_u16 }
cast_uint! { u32, 32, small::unchecked_get_unshifted_u32 }
cast_uint! { u64, 64, small::unchecked_get_unshifted_u64 }
cast_uint! { u128, 128, small::unchecked_get_unshifted_u128 }
cast_int_uint_common! { usize }
impl CheckedCast<usize> for &'_ Float {
#[inline]
fn checked_cast(self) -> Option<usize> {
#[cfg(target_pointer_width = "32")]
{
self.checked_as::<u32>().map(az::cast)
}
#[cfg(target_pointer_width = "64")]
{
self.checked_as::<u64>().map(az::cast)
}
}
}
impl Cast<f32> for Float {
#[inline]
fn cast(self) -> f32 {
(&self).cast()
}
}
impl Cast<f32> for &'_ Float {
#[inline]
fn cast(self) -> f32 {
self.to_f32_round(Round::Nearest)
}
}
impl Cast<f64> for Float {
#[inline]
fn cast(self) -> f64 {
(&self).cast()
}
}
impl Cast<f64> for &'_ Float {
#[inline]
fn cast(self) -> f64 {
self.to_f64_round(Round::Nearest)
}
}
#[cfg(feature = "integer")]
impl Cast<Integer> for Float {
#[inline]
fn cast(self) -> Integer {
(&self).cast()
}
}
#[cfg(feature = "integer")]
impl Cast<Integer> for &'_ Float {
#[inline]
fn cast(self) -> Integer {
self.checked_cast().expect("not finite")
}
}
#[cfg(feature = "integer")]
impl CheckedCast<Integer> for Float {
#[inline]
fn checked_cast(self) -> Option<Integer> {
(&self).checked_cast()
}
}
#[cfg(feature = "integer")]
impl CheckedCast<Integer> for &'_ Float {
#[inline]
fn checked_cast(self) -> Option<Integer> {
self.to_integer_round(Round::Nearest).map(|x| x.0)
}
}
#[cfg(feature = "integer")]
impl UnwrappedCast<Integer> for Float {
#[inline]
fn unwrapped_cast(self) -> Integer {
(&self).unwrapped_cast()
}
}
#[cfg(feature = "integer")]
impl UnwrappedCast<Integer> for &'_ Float {
#[inline]
fn unwrapped_cast(self) -> Integer {
self.checked_cast().expect("not finite")
}
}
#[cfg(feature = "rational")]
impl Cast<Rational> for Float {
#[inline]
fn cast(self) -> Rational {
(&self).cast()
}
}
#[cfg(feature = "rational")]
impl Cast<Rational> for &'_ Float {
#[inline]
fn cast(self) -> Rational {
self.checked_cast().expect("not finite")
}
}
#[cfg(feature = "rational")]
impl CheckedCast<Rational> for Float {
#[inline]
fn checked_cast(self) -> Option<Rational> {
(&self).checked_cast()
}
}
#[cfg(feature = "rational")]
impl CheckedCast<Rational> for &'_ Float {
#[inline]
fn checked_cast(self) -> Option<Rational> {
if !self.is_finite() {
return None;
}
let mut r = Rational::new();
xmpfr::get_q(&mut r, self);
Some(r)
}
}
#[cfg(feature = "rational")]
impl UnwrappedCast<Rational> for Float {
#[inline]
fn unwrapped_cast(self) -> Rational {
(&self).unwrapped_cast()
}
}
#[cfg(feature = "rational")]
impl UnwrappedCast<Rational> for &'_ Float {
#[inline]
fn unwrapped_cast(self) -> Rational {
self.checked_cast().expect("not finite")
}
}
#[cfg(test)]
#[allow(clippy::float_cmp)]
mod tests {
use crate::{Assign, Float};
use az::{Az, SaturatingAs, SaturatingCast};
use core::borrow::Borrow;
use core::fmt::Debug;
use core::ops::{Add, Sub};
fn check_integer<T>(min: T, max: T)
where
T: Copy + Debug + Eq + Add<Output = T> + Sub<Output = T> + From<bool>,
Float: Assign<T>,
for<'a> &'a Float: SaturatingCast<T>,
{
let min_float = Float::with_val(128, min);
let max_float = Float::with_val(128, max);
let one = T::from(true);
assert_eq!(
(min_float.clone() - 1f32).borrow().saturating_as::<T>(),
min
);
assert_eq!(
(min_float.clone() - 0.5f32).borrow().saturating_as::<T>(),
min
);
assert_eq!(min_float.borrow().saturating_as::<T>(), min);
assert_eq!(
(min_float.clone() + 0.5f32).borrow().saturating_as::<T>(),
min
);
assert_eq!((min_float + 1f32).borrow().saturating_as::<T>(), min + one);
assert_eq!(
(max_float.clone() - 1f32).borrow().saturating_as::<T>(),
max - one
);
assert_eq!(
(max_float.clone() - 0.5f32).borrow().saturating_as::<T>(),
max - one
);
assert_eq!(max_float.borrow().saturating_as::<T>(), max);
assert_eq!(
(max_float.clone() + 0.5f32).borrow().saturating_as::<T>(),
max
);
assert_eq!((max_float + 1f32).borrow().saturating_as::<T>(), max);
}
#[test]
fn check_integers() {
check_integer(i8::MIN, i8::MAX);
check_integer(i16::MIN, i16::MAX);
check_integer(i32::MIN, i32::MAX);
check_integer(i64::MIN, i64::MAX);
check_integer(i128::MIN, i128::MAX);
check_integer(isize::MIN, isize::MAX);
check_integer(u8::MIN, u8::MAX);
check_integer(u16::MIN, u16::MAX);
check_integer(u32::MIN, u32::MAX);
check_integer(u64::MIN, u64::MAX);
check_integer(u128::MIN, u128::MAX);
check_integer(usize::MIN, usize::MAX);
}
#[test]
fn check_floats() {
let f32_min_pos_subnormal: Float = Float::with_val(128, 1) >> (126 + 23);
let f32_min_pos_normal: Float = Float::with_val(128, 1) >> 126;
let f32_max: Float = Float::with_val(128, (1u32 << 24) - 1) << (127 - 23);
let f64_min_pos_subnormal: Float = Float::with_val(128, 1) >> (1022 + 52);
let f64_min_pos_normal: Float = Float::with_val(128, 1) >> 1022;
let f64_max: Float = Float::with_val(128, (1u64 << 53) - 1) << (1023 - 52);
let zero: Float = Float::new(1);
let one: Float = Float::with_val(1, 1);
let two_point5: Float = Float::with_val(3, 2.5);
let f32_overflow: Float = Float::with_val(1, 1) << 128;
let f64_overflow: Float = Float::with_val(1, 1) << 1024;
assert_eq!(
(*f32_overflow.as_neg()).borrow().az::<f32>(),
f32::NEG_INFINITY
);
assert_eq!((*f32_max.as_neg()).borrow().az::<f32>(), -f32::MAX);
assert_eq!((*two_point5.as_neg()).borrow().az::<f32>(), -2.5f32);
assert_eq!((*one.as_neg()).borrow().az::<f32>(), -1f32);
assert_eq!(
(*f32_min_pos_normal.as_neg()).borrow().az::<f32>(),
-f32::MIN_POSITIVE
);
assert_eq!(
(*f32_min_pos_subnormal.as_neg()).borrow().az::<f32>(),
-f32::from_bits(1)
);
assert_eq!((*zero.as_neg()).borrow().az::<f32>(), 0f32);
assert!((*zero.as_neg()).borrow().az::<f32>().is_sign_negative());
assert!(zero.borrow().az::<f32>().is_sign_positive());
assert_eq!(zero.borrow().az::<f32>(), 0f32);
assert_eq!(
(*f32_min_pos_subnormal.as_neg()).borrow().az::<f32>(),
-f32::from_bits(1)
);
assert_eq!(
(*f32_min_pos_normal.as_neg()).borrow().az::<f32>(),
-f32::MIN_POSITIVE
);
assert_eq!(one.borrow().az::<f32>(), 1f32);
assert_eq!(two_point5.borrow().az::<f32>(), 2.5f32);
assert_eq!(f32_max.borrow().az::<f32>(), f32::MAX);
assert_eq!(f32_overflow.borrow().az::<f32>(), f32::INFINITY);
assert_eq!(
(*f64_overflow.as_neg()).borrow().az::<f64>(),
f64::NEG_INFINITY
);
assert_eq!((*f64_max.as_neg()).borrow().az::<f64>(), -f64::MAX);
assert_eq!((*two_point5.as_neg()).borrow().az::<f64>(), -2.5f64);
assert_eq!((*one.as_neg()).borrow().az::<f64>(), -1f64);
assert_eq!(
(*f64_min_pos_normal.as_neg()).borrow().az::<f64>(),
-f64::MIN_POSITIVE
);
assert_eq!(
(*f64_min_pos_subnormal.as_neg()).borrow().az::<f64>(),
-f64::from_bits(1)
);
assert_eq!((*zero.as_neg()).borrow().az::<f64>(), 0f64);
assert!((*zero.as_neg()).borrow().az::<f64>().is_sign_negative());
assert!(zero.borrow().az::<f64>().is_sign_positive());
assert_eq!(zero.borrow().az::<f64>(), 0f64);
assert_eq!(
(*f64_min_pos_subnormal.as_neg()).borrow().az::<f64>(),
-f64::from_bits(1)
);
assert_eq!(
(*f64_min_pos_normal.as_neg()).borrow().az::<f64>(),
-f64::MIN_POSITIVE
);
assert_eq!(one.borrow().az::<f64>(), 1f64);
assert_eq!(two_point5.borrow().az::<f64>(), 2.5f64);
assert_eq!(f64_max.borrow().az::<f64>(), f64::MAX);
assert_eq!(f64_overflow.borrow().az::<f64>(), f64::INFINITY);
}
}