#![allow(unused_macros)]
#[cfg(test)]
#[allow(dead_code)] #[allow(clippy::eq_op)] pub fn is_nan<T>(val: T) -> bool
where
T: PartialEq,
{
val != val
}
macro_rules! is_infinite {
($value:expr, $type:ty) => {{
let v = $value;
#[allow(clippy::manual_range_contains)]
((v > <$type>::MAX) || (v < <$type>::MIN))
}};
}
macro_rules! SL {
($($tts:tt)*) => { stringify!($($tts)*) };
}
macro_rules! as_expr {
($e:expr) => {
$e
};
}
macro_rules! check {
(@ $from:ty, $to:ty=> $(;)*) => {};
(@ $from:ty, $to:ty=> uident; $($tail:tt)*) => {
check!(@ $from, $to=> v: 0;);
check!(@ $from, $to=> v: 1;);
check!(@ $from, $to=> $($tail)*);
};
(@ $from:ty, $to:ty=> sident; $($tail:tt)*) => {
check!(@ $from, $to=> v: -1;);
check!(@ $from, $to=> v: 0;);
check!(@ $from, $to=> v: 1;);
check!(@ $from, $to=> $($tail)*);
};
(@ $from:ty, $to:ty=> fident; $($tail:tt)*) => {
check!(@ $from, $to=> v: -1.0;);
check!(@ $from, $to=> v: 0.0;);
check!(@ $from, $to=> v: 1.0;);
check!(@ $from, $to=> $($tail)*);
};
(@ $from:ty, $to:ty=> uidenta; $($tail:tt)*) => {
check!(@ $from, $to=> a: 0.0;);
check!(@ $from, $to=> a: 1.0;);
check!(@ $from, $to=> aRTN: 0.00, 0;);
check!(@ $from, $to=> aRTN: 0.25, 0;);
check!(@ $from, $to=> aRTN: 0.50, 1;);
check!(@ $from, $to=> aRTN: 0.75, 1;);
check!(@ $from, $to=> aRTN: 1.00, 1;);
check!(@ $from, $to=> aRNI: 0.00, 0;);
check!(@ $from, $to=> aRNI: 0.25, 0;);
check!(@ $from, $to=> aRNI: 0.50, 0;);
check!(@ $from, $to=> aRNI: 0.75, 0;);
check!(@ $from, $to=> aRNI: 1.00, 1;);
check!(@ $from, $to=> aRPI: 0.00, 0;);
check!(@ $from, $to=> aRPI: 0.25, 1;);
check!(@ $from, $to=> aRPI: 0.50, 1;);
check!(@ $from, $to=> aRPI: 0.75, 1;);
check!(@ $from, $to=> aRPI: 1.00, 1;);
check!(@ $from, $to=> aRTZ: 0.00, 0;);
check!(@ $from, $to=> aRTZ: 0.25, 0;);
check!(@ $from, $to=> aRTZ: 0.50, 0;);
check!(@ $from, $to=> aRTZ: 0.75, 0;);
check!(@ $from, $to=> aRTZ: 1.00, 1;);
check!(@ $from, $to=> $($tail)*);
};
(@ $from:ty, $to:ty=> sidenta; $($tail:tt)*) => {
check!(@ $from, $to=> a: -1.0;);
check!(@ $from, $to=> a: 0.0;);
check!(@ $from, $to=> a: 1.0;);
check!(@ $from, $to=> aRTN: -1.00, -1;);
check!(@ $from, $to=> aRTN: -0.75, -1;);
check!(@ $from, $to=> aRTN: -0.50, -1;);
check!(@ $from, $to=> aRTN: -0.25, 0;);
check!(@ $from, $to=> aRTN: 0.00, 0;);
check!(@ $from, $to=> aRTN: 0.25, 0;);
check!(@ $from, $to=> aRTN: 0.50, 1;);
check!(@ $from, $to=> aRTN: 0.75, 1;);
check!(@ $from, $to=> aRTN: 1.00, 1;);
check!(@ $from, $to=> aRNI: -1.00, -1;);
check!(@ $from, $to=> aRNI: -0.75, -1;);
check!(@ $from, $to=> aRNI: -0.50, -1;);
check!(@ $from, $to=> aRNI: -0.25, -1;);
check!(@ $from, $to=> aRNI: 0.00, 0;);
check!(@ $from, $to=> aRNI: 0.25, 0;);
check!(@ $from, $to=> aRNI: 0.50, 0;);
check!(@ $from, $to=> aRNI: 0.75, 0;);
check!(@ $from, $to=> aRNI: 1.00, 1;);
check!(@ $from, $to=> aRPI: -1.00, -1;);
check!(@ $from, $to=> aRPI: -0.75, 0;);
check!(@ $from, $to=> aRPI: -0.50, 0;);
check!(@ $from, $to=> aRPI: -0.25, 0;);
check!(@ $from, $to=> aRPI: 0.00, 0;);
check!(@ $from, $to=> aRPI: 0.25, 1;);
check!(@ $from, $to=> aRPI: 0.50, 1;);
check!(@ $from, $to=> aRPI: 0.75, 1;);
check!(@ $from, $to=> aRPI: 1.00, 1;);
check!(@ $from, $to=> aRTZ: -1.00, -1;);
check!(@ $from, $to=> aRTZ: -0.75, 0;);
check!(@ $from, $to=> aRTZ: -0.50, 0;);
check!(@ $from, $to=> aRTZ: -0.25, 0;);
check!(@ $from, $to=> aRTZ: 0.00, 0;);
check!(@ $from, $to=> aRTZ: 0.25, 0;);
check!(@ $from, $to=> aRTZ: 0.50, 0;);
check!(@ $from, $to=> aRTZ: 0.75, 0;);
check!(@ $from, $to=> aRTZ: 1.00, 1;);
check!(@ $from, $to=> $($tail)*);
};
(@ $from:ty, $to:ty=> fidenta; $($tail:tt)*) => {
check!(@ $from, $to=> a: -1.0;);
check!(@ $from, $to=> a: 0.0;);
check!(@ $from, $to=> a: 1.0;);
check!(@ $from, $to=> $($tail)*);
};
(@ $from:ty, $to:ty=> v: $src:expr, !$dst:expr; $($tail:tt)*) => {
{
println!("? {} => {}, v: {}, !{}", SL!($from), SL!($to), SL!($src), SL!($dst));
let src: $from = $src;
let dst: Result<$to, _> = src.value_into();
assert_eq!(dst, Err($dst(src)));
}
check!(@ $from, $to=> $($tail)*);
};
(@ $from:ty, $to:ty=> v: $src:expr; $($tail:tt)*) => {
{
println!("? {} => {}, v: {}", SL!($from), SL!($to), SL!($src));
let src: $from = $src;
let dst: Result<$to, _> = src.value_into();
if util::is_nan(src) {
assert!(util::is_nan(dst.expect("error but expected NaN")));
} else {
assert_eq!(dst, Ok($src as $to));
}
}
check!(@ $from, $to=> $($tail)*);
};
(@ $from:ty, $to:ty=> qv: *; $($tail:tt)*) => {
{
println!("? {} => {}, qv: *", SL!($from), SL!($to));
fn property(v: $from) -> bool {
let dst: Result<$to, _> = v.value_into();
if util::is_nan(v) {
util::is_nan(dst)
} else {
dst == Ok(v as $to)
}
}
let mut qc = quickcheck::QuickCheck::new();
match qc.quicktest(property as fn($from) -> bool) {
Ok(_) => (),
Err(err) => panic!("qv1 {err:?}")
}
}
check!(@ $from, $to=> $($tail)*);
};
(@ $from:ty, $to:ty=> qv: (+-$bound:expr); $($tail:tt)*) => {
{
println!("? {} => {}, qv: (+- {})", SL!($from), SL!($to), SL!($bound));
fn property(v: $from) -> bool {
let dst: Result<$to, conv2::FloatError<_>> = v.value_into().map_err(From::from);
if v < -$bound as $from {
dst == Err(conv2::FloatError::NegOverflow(v))
} else if v > $bound as $from {
dst == Err(conv2::FloatError::PosOverflow(v))
} else {
dst == Ok(v as $to)
}
}
let mut qc = quickcheck::QuickCheck::new();
match qc.quicktest(property as fn($from) -> bool) {
Ok(_) => (),
Err(err) => panic!("qv2 {err:?}")
}
}
check!(@ $from, $to=> $($tail)*);
};
(@ $from:ty, $to:ty=> qv: (, $bound:expr); $($tail:tt)*) => {
{
println!("? {} => {}, qv: (, {})", SL!($from), SL!($to), SL!($bound));
fn property(v: $from) -> bool {
let dst: Result<$to, conv2::FloatError<_>> = v.value_into().map_err(From::from);
if v > $bound as $from {
dst == Err(conv2::FloatError::PosOverflow(v))
} else {
dst == Ok(v as $to)
}
}
let mut qc = quickcheck::QuickCheck::new();
match qc.quicktest(property as fn($from) -> bool) {
Ok(_) => (),
Err(err) => panic!("qv3 {err:?}")
}
}
check!(@ $from, $to=> $($tail)*);
};
(@ $from:ty, $to:ty=> qv: +; $($tail:tt)*) => {
{
println!("? {} => {}, qv: +", SL!($from), SL!($to));
fn property(v: $from) -> bool {
let dst: Result<$to, conv2::FloatError<_>> = v.value_into().map_err(From::from);
if v < 0 {
dst == Err(conv2::FloatError::NegOverflow(v))
} else {
dst == Ok(v as $to)
}
}
let mut qc = quickcheck::QuickCheck::new();
match qc.quicktest(property as fn($from) -> bool) {
Ok(_) => (),
Err(err) => panic!("qv4 {err:?}")
}
}
check!(@ $from, $to=> $($tail)*);
};
(@ $from:ty, $to:ty=> qv: +$max:ty=> $($tail:tt)*) => {
{
println!("? {} => {}, qv: +{}", SL!($from), SL!($to), SL!($max));
fn property(v: $from) -> bool {
let dst: Result<$to, conv2::FloatError<_>> = v.value_into().map_err(From::from);
let max = <$max>::MAX as $from;
if v > max {
dst == Err(conv2::FloatError::PosOverflow(v))
} else {
dst == Ok(v as $to)
}
}
let mut qc = quickcheck::QuickCheck::new();
match qc.quicktest(property as fn($from) -> bool) {
Ok(_) => (),
Err(err) => panic!("qv5 {err:?}")
}
}
check!(@ $from, $to=> $($tail)*);
};
(@ $from:ty, $to:ty=> qv: $bound:ty=> $($tail:tt)*) => {
{
println!("? {} => {}, qv: {}", SL!($from), SL!($to), SL!($bound));
fn property(v: $from) -> bool {
let dst: Result<$to, conv2::FloatError<_>> = v.value_into().map_err(From::from);
let min = <$bound>::MIN as $from;
let max = <$bound>::MAX as $from;
if v < min {
dst == Err(conv2::FloatError::NegOverflow(v))
} else if v > max {
dst == Err(conv2::FloatError::PosOverflow(v))
} else {
dst == Ok(v as $to)
}
}
let mut qc = quickcheck::QuickCheck::new();
match qc.quicktest(property as fn($from) -> bool) {
Ok(_) => (),
Err(err) => panic!("qv6 {err:?}")
}
}
check!(@ $from, $to=> $($tail)*);
};
(@ $from:ty, $to:ty=> qv: $min:ty, $max:ty=> $($tail:tt)*) => {
{
println!("? {} => {}, qv: {}, {}", SL!($from), SL!($to), SL!($min), SL!($max));
fn property(v: $from) -> bool {
let dst: Result<$to, conv2::FloatError<_>> = v.value_into().map_err(From::from);
let min = <$min>::MIN as $from;
let max = <$max>::MAX as $from;
if v < min {
dst == Err(conv2::FloatError::NegOverflow(v))
} else if v > max {
dst == Err(conv2::FloatError::PosOverflow(v))
} else {
dst == Ok(v as $to)
}
}
let mut qc = quickcheck::QuickCheck::new();
match qc.quicktest(property as fn($from) -> bool) {
Ok(_) => (),
Err(err) => panic!("qv7 {err:?}")
}
}
check!(@ $from, $to=> $($tail)*);
};
(@ $from:ty, $to:ty=> a: $src:expr, !$dst:expr; $($tail:tt)*) => {
{
println!("? {} => {}, a: {}, !{}", SL!($from), SL!($to), SL!($src), SL!($dst));
let src: $from = $src;
let dst: Result<$to, _> = src.approx_as();
assert_eq!(dst, Err($dst(src)));
}
check!(@ $from, $to=> $($tail)*);
};
(@ $from:ty, $to:ty=> a: $src:expr, $dst:expr; $($tail:tt)*) => {
{
println!("? {} => {}, a: {}, {}", SL!($from), SL!($to), SL!($src), SL!($dst));
let src: $from = $src;
let dst: Result<$to, _> = src.approx_as();
assert_eq!(dst, Ok($dst));
}
check!(@ $from, $to=> $($tail)*);
};
(@ $from:ty, $to:ty=> a: $src:expr; $($tail:tt)*) => {
{
println!("? {} => {}, a: {}", SL!($from), SL!($to), SL!($src));
let src: $from = $src;
let dst: Result<$to, _> = src.approx_as();
assert_eq!(dst, Ok($src as $to));
}
check!(@ $from, $to=> $($tail)*);
};
(@ $from:ty, $to:ty=> qa: *; $($tail:tt)*) => {
{
println!("? {} => {}, qa: *", SL!($from), SL!($to));
fn property(v: $from) -> bool {
let dst: Result<$to, _> = v.approx_as();
if util::is_nan(v) {
util::is_nan(dst.expect("error but expected NaN"))
} else {
dst == Ok(v as $to)
}
}
let mut qc = quickcheck::QuickCheck::new();
match qc.quicktest(property as fn($from) -> bool) {
Ok(_) => (),
Err(err) => panic!("qa1 {err:?}")
}
}
check!(@ $from, $to=> $($tail)*);
};
(@ $from:ty, $to:ty=> qa: +; $($tail:tt)*) => {
{
println!("? {} => {}, qa: +", SL!($from), SL!($to));
fn property(v: $from) -> bool {
let dst: Result<$to, conv2::FloatError<_>> = v.approx_as().map_err(From::from);
if v < 0 {
dst == Err(conv2::FloatError::NegOverflow(v))
} else {
dst == Ok(v as $to)
}
}
let mut qc = quickcheck::QuickCheck::new();
match qc.quicktest(property as fn($from) -> bool) {
Ok(_) => (),
Err(err) => panic!("qa2 {err:?}")
}
}
check!(@ $from, $to=> $($tail)*);
};
(@ $from:ty, $to:ty=> qa: +$max:ty=> $($tail:tt)*) => {
{
println!("? {} => {}, qa: +{}", SL!($from), SL!($to), SL!($max));
fn property(v: $from) -> bool {
let dst: Result<$to, conv2::FloatError<_>> = v.approx_as().map_err(From::from);
let max = <$max>::MAX as $from;
if v > max {
dst == Err(conv2::FloatError::PosOverflow(v))
} else {
dst == Ok(v as $to)
}
}
let mut qc = quickcheck::QuickCheck::new();
match qc.quicktest(property as fn($from) -> bool) {
Ok(_) => (),
Err(err) => panic!("qa3 {err:?}")
}
}
check!(@ $from, $to=> $($tail)*);
};
(@ $from:ty, $to:ty=> qa: $bound:ty=> $($tail:tt)*) => {
{
println!("? {} => {}, qa: {}", SL!($from), SL!($to), SL!($bound));
fn property(v: $from) -> bool {
let dst: Result<$to, conv2::FloatError<_>> = v.approx_as().map_err(From::from);
let min = <$bound>::MIN as $from;
let max = <$bound>::MAX as $from;
if util::is_nan(v) {
dst.is_err() || util::is_nan(dst)
} else if is_infinite!(v, $from) && dst.is_ok() {
dst == Ok(v as $to)
} else if v < min {
dst == Err(conv2::FloatError::NegOverflow(v))
} else if v > max {
dst == Err(conv2::FloatError::PosOverflow(v))
} else {
dst == Ok(v as $to)
}
}
let mut qc = quickcheck::QuickCheck::new();
match qc.quicktest(property as fn($from) -> bool) {
Ok(_) => (),
Err(err) => panic!("qa4 {err:?}")
}
}
check!(@ $from, $to=> $($tail)*);
};
(@ $from:ty, $to:ty=> qaW: *; $($tail:tt)*) => {
{
println!("? {} => {}, qaW: *", SL!($from), SL!($to));
fn property(v: $from) -> bool {
let dst: Result<$to, _> = v.approx_as_by::<_, Wrapping>();
dst == Ok(v as $to)
}
let mut qc = quickcheck::QuickCheck::new();
match qc.quicktest(property as fn($from) -> bool) {
Ok(_) => (),
Err(err) => panic!("qaW {err:?}")
}
}
check!(@ $from, $to=> $($tail)*);
};
(@ $from:ty, $to:ty=> aRTN: $src:expr, $dst:expr; $($tail:tt)*) => {
{
println!("? {} => {}, aRTN: {}, {}", SL!($from), SL!($to), SL!($src), SL!($dst));
let src: $from = $src;
let dst: Result<$to, _> = src.approx_by::<conv2::RoundToNearest>();
assert_eq!(dst, Ok($dst));
}
check!(@ $from, $to=> $($tail)*);
};
(@ $from:ty, $to:ty=> aRNI: $src:expr, $dst:expr; $($tail:tt)*) => {
{
println!("? {} => {}, aRNI: {}, {}", SL!($from), SL!($to), SL!($src), SL!($dst));
let src: $from = $src;
let dst: Result<$to, _> = src.approx_by::<conv2::RoundToNegInf>();
assert_eq!(dst, Ok($dst));
}
check!(@ $from, $to=> $($tail)*);
};
(@ $from:ty, $to:ty=> aRPI: $src:expr, $dst:expr; $($tail:tt)*) => {
{
println!("? {} => {}, aRPI: {}, {}", SL!($from), SL!($to), SL!($src), SL!($dst));
let src: $from = $src;
let dst: Result<$to, _> = src.approx_by::<conv2::RoundToPosInf>();
assert_eq!(dst, Ok($dst));
}
check!(@ $from, $to=> $($tail)*);
};
(@ $from:ty, $to:ty=> aRTZ: $src:expr, $dst:expr; $($tail:tt)*) => {
{
println!("? {} => {}, aRTZ: {}, {}", SL!($from), SL!($to), SL!($src), SL!($dst));
let src: $from = $src;
let dst: Result<$to, _> = src.approx_by::<conv2::RoundToZero>();
assert_eq!(dst, Ok($dst));
}
check!(@ $from, $to=> $($tail)*);
};
($from:ty, $to:ty=> $($tail:tt)*) => {
check! { @ $from, $to=> $($tail)*; }
};
}
macro_rules! for_bitness {
(32 {$($bits32:tt)*} 64 {$($bits64:tt)*}) => {
as_expr!(
{
#[cfg(target_pointer_width="32")]
fn for_bitness() {
$($bits32)*
}
#[cfg(target_pointer_width="64")]
fn for_bitness() {
$($bits64)*
}
for_bitness()
}
)
};
}