#[cfg(feature = "f16")]
use super::f016::{ceil_f64_f16, floor_f64_f16};
use super::f032::{F032I008, F032I016, F032U008, F032U016};
#[cfg(test)]
#[allow(unused_imports)]
use crate::conn::Conn;
#[cfg(feature = "f16")]
use crate::float::F016;
use crate::float::{
F032, F064, N5, def_walk_helpers, float_ext_int, float_ext_int_l, shift32, widen_f32_f64,
};
def_walk_helpers!(f64_f32_walks, f64, f32, shift32, widen_f32_f64);
fn f064f032_ceil(x: F064) -> F032 {
N5::new(ceil_f64_f32(x.into_inner()))
}
#[cfg(feature = "f16")]
fn f064f016_ceil(x: F064) -> F016 {
N5::new(ceil_f64_f16(x.into_inner()))
}
#[cfg(feature = "f16")]
fn f064f016_inner(y: F016) -> F064 {
N5::new(y.into_inner() as f64)
}
#[cfg(feature = "f16")]
fn f064f016_floor(x: F064) -> F016 {
N5::new(floor_f64_f16(x.into_inner()))
}
#[cfg(feature = "f16")]
#[cfg_attr(docsrs, doc(cfg(feature = "f16")))]
crate::conn_k! {
pub F064F016 : F064 => F016 {
ceil: f064f016_ceil,
inner: f064f016_inner,
floor: f064f016_floor,
}
}
fn f064f032_inner(y: F032) -> F064 {
N5::new(y.into_inner() as f64)
}
fn f064f032_floor(x: F064) -> F032 {
N5::new(floor_f64_f32(x.into_inner()))
}
crate::conn_k! {
pub F064F032 : F064 => F032 {
ceil: f064f032_ceil,
inner: f064f032_inner,
floor: f064f032_floor,
}
}
fn ceil_f64_f32(x: f64) -> f32 {
if x.is_nan() {
return f32::NAN;
}
let est = x as f32;
let est_up = est as f64;
if est_up == x {
return est;
}
let (z, _steps) = if x <= est_up {
f64_f32_walks::descend_to_ceil(est, x)
} else {
f64_f32_walks::ascend_to_ceil(est, x)
};
z
}
fn floor_f64_f32(x: f64) -> f32 {
if x.is_nan() {
return f32::NAN;
}
let est = x as f32;
let est_up = est as f64;
if est_up == x {
return est;
}
let (z, _steps) = if est_up <= x {
f64_f32_walks::ascend_to_floor(est, x)
} else {
f64_f32_walks::descend_to_floor(est, x)
};
z
}
#[cfg(kani)]
pub(crate) fn ceil_walk_steps_for_proof(x: f64) -> (f32, u32) {
let est = x as f32;
let est_up = est as f64;
if est_up == x {
return (est, 0);
}
if x <= est_up {
f64_f32_walks::descend_to_ceil(est, x)
} else {
f64_f32_walks::ascend_to_ceil(est, x)
}
}
#[cfg(kani)]
pub(crate) fn floor_walk_steps_for_proof(x: f64) -> (f32, u32) {
let est = x as f32;
let est_up = est as f64;
if est_up == x {
return (est, 0);
}
if est_up <= x {
f64_f32_walks::ascend_to_floor(est, x)
} else {
f64_f32_walks::descend_to_floor(est, x)
}
}
float_ext_int! (
pub F064U032, f64, u32
);
float_ext_int_l!(
pub F064U064, f64, u64
);
float_ext_int! (
pub F064I032, f64, i32
);
float_ext_int_l!(
pub F064I064, f64, i64
);
crate::compose_k!(
pub F064U008 : F064 => F032 => crate::extended::Extended<u8> = F064F032, F032U008
);
crate::compose_k!(
pub F064U016 : F064 => F032 => crate::extended::Extended<u16> = F064F032, F032U016
);
crate::compose_k!(
pub F064I008 : F064 => F032 => crate::extended::Extended<i8> = F064F032, F032I008
);
crate::compose_k!(
pub F064I016 : F064 => F032 => crate::extended::Extended<i16> = F064F032, F032I016
);
#[cfg(test)]
mod tests {
use super::*;
#[allow(unused_imports)]
use crate::conn::{ConnL, ConnR};
use crate::prop::arb::{arb_f32, arb_f64};
use crate::prop::conn as conn_laws;
use proptest::prelude::*;
fn ef64() -> impl Strategy<Value = N5<f64>> {
prop_oneof![
1 => Just(N5::new(f64::NAN)),
1 => Just(N5::new(f64::NEG_INFINITY)),
1 => Just(N5::new(f64::INFINITY)),
8 => arb_f64().prop_map(N5::new),
]
}
fn ef32() -> impl Strategy<Value = N5<f32>> {
prop_oneof![
1 => Just(N5::new(f32::NAN)),
1 => Just(N5::new(f32::NEG_INFINITY)),
1 => Just(N5::new(f32::INFINITY)),
8 => arb_f32().prop_map(N5::new),
]
}
#[test]
fn ceil_exact_value() {
assert_eq!(F064F032.view_l().ceil(N5::new(0.5_f64)), N5::new(0.5_f32));
}
#[test]
fn floor_exact_value() {
assert_eq!(F064F032.view_r().floor(N5::new(0.5_f64)), N5::new(0.5_f32));
}
#[test]
fn ceil_nan() {
assert!(
F064F032
.view_l()
.ceil(N5::new(f64::NAN))
.into_inner()
.is_nan()
);
}
#[test]
fn floor_nan() {
assert!(
F064F032
.view_r()
.floor(N5::new(f64::NAN))
.into_inner()
.is_nan()
);
}
#[test]
fn inner_nan() {
assert!(
F064F032
.view_l()
.upper(N5::new(f32::NAN))
.into_inner()
.is_nan()
);
}
#[test]
fn ceil_ge_floor() {
let x = N5::new(std::f64::consts::PI);
assert!(F064F032.view_r().floor(x) <= F064F032.view_l().ceil(x));
}
#[test]
fn infinities_pass_through() {
assert_eq!(
F064F032.view_l().ceil(N5::new(f64::NEG_INFINITY)),
N5::new(f32::NEG_INFINITY)
);
assert_eq!(
F064F032.view_r().floor(N5::new(f64::NEG_INFINITY)),
N5::new(f32::NEG_INFINITY)
);
assert_eq!(
F064F032.view_l().ceil(N5::new(f64::INFINITY)),
N5::new(f32::INFINITY)
);
assert_eq!(
F064F032.view_r().floor(N5::new(f64::INFINITY)),
N5::new(f32::INFINITY)
);
assert_eq!(
F064F032.view_l().upper(N5::new(f32::NEG_INFINITY)),
N5::new(f64::NEG_INFINITY)
);
assert_eq!(
F064F032.view_l().upper(N5::new(f32::INFINITY)),
N5::new(f64::INFINITY)
);
}
crate::law_battery! { mod laws, conn: F064F032, fine: ef64(), coarse: ef32(), subset: numeric_only, }
proptest! {
#[test]
fn floor_le_ceil(a in ef64()) {
prop_assert!(conn_laws::floor_le_ceil(&F064F032, a));
}
#[test]
fn ulp_steps_bounded(x in arb_f64()) {
if x.is_nan() {
return Ok(());
}
let est = x as f32;
let est_up = est as f64;
if est_up == x {
return Ok(());
}
let (_, steps) = if x <= est_up {
f64_f32_walks::descend_to_ceil(est, x)
} else {
f64_f32_walks::ascend_to_ceil(est, x)
};
prop_assert!(steps <= 2, "f64_f32_walks::ascend/descend_to_ceil took {steps} steps on x={x}");
let (_, steps) = if est_up <= x {
f64_f32_walks::ascend_to_floor(est, x)
} else {
f64_f32_walks::descend_to_floor(est, x)
};
prop_assert!(steps <= 2, "f64_f32_walks::ascend/descend_to_floor took {steps} steps on x={x}");
}
}
use crate::extended::Extended;
use crate::prop::arb::{
arb_extended_i8, arb_extended_i16, arb_extended_i32, arb_extended_i64, arb_extended_u8,
arb_extended_u16, arb_extended_u32, arb_extended_u64, extended_float_f64,
};
#[test]
fn f064i032_exact_int() {
assert_eq!(
F064I032.view_l().ceil(N5::new(2.5_f64)),
Extended::Finite(3_i32)
);
assert_eq!(
F064I032.view_r().floor(N5::new(2.5_f64)),
Extended::Finite(2_i32)
);
}
#[test]
fn f064u064_saturate_high() {
let huge = N5::new(1.0e30_f64);
assert_eq!(F064U064.ceil(huge), Extended::PosInf);
}
#[test]
fn f064i064_saturate_low() {
let huge_neg = N5::new(-1.0e30_f64);
assert_eq!(F064I064.ceil(huge_neg), Extended::Finite(i64::MIN));
}
#[test]
fn f064u064_at_plateau_to_posinf() {
let plateau = N5::new(2.0_f64.powi(64));
assert_eq!(F064U064.ceil(plateau), Extended::PosInf);
}
#[test]
fn f064i064_at_plateau_to_posinf() {
let plateau = N5::new(2.0_f64.powi(63));
assert_eq!(F064I064.ceil(plateau), Extended::PosInf);
}
#[test]
fn f064u064_just_past_plateau_to_posinf() {
let v = f64::from_bits(2.0_f64.powi(64).to_bits() + 1);
assert_eq!(F064U064.ceil(N5::new(v)), Extended::PosInf,);
}
#[test]
fn f064i064_just_past_plateau_to_posinf() {
let v = f64::from_bits(2.0_f64.powi(63).to_bits() + 1);
assert_eq!(F064I064.ceil(N5::new(v)), Extended::PosInf,);
}
#[test]
fn f064u008_composed_matches_direct_path() {
let v = N5::new(42.7_f64);
assert_eq!(F064U008.view_l().ceil(v), Extended::Finite(43));
assert_eq!(F064U008.view_r().floor(v), Extended::Finite(42));
}
crate::law_battery! { mod laws_u032, conn: F064U032, fine: extended_float_f64(), coarse: arb_extended_u32(), cases: 1024, }
crate::law_battery! { mod laws_u064, conn: F064U064, fine: extended_float_f64(), coarse: arb_extended_u64(), subset: l_only, cases: 1024, }
crate::law_battery! { mod laws_i032, conn: F064I032, fine: extended_float_f64(), coarse: arb_extended_i32(), cases: 1024, }
crate::law_battery! { mod laws_i064, conn: F064I064, fine: extended_float_f64(), coarse: arb_extended_i64(), subset: l_only, cases: 1024, }
crate::law_battery! { mod laws_u008_composed, conn: F064U008, fine: extended_float_f64(), coarse: arb_extended_u8(), cases: 1024, }
crate::law_battery! { mod laws_u016_composed, conn: F064U016, fine: extended_float_f64(), coarse: arb_extended_u16(), cases: 1024, }
crate::law_battery! { mod laws_i008_composed, conn: F064I008, fine: extended_float_f64(), coarse: arb_extended_i8(), cases: 1024, }
crate::law_battery! { mod laws_i016_composed, conn: F064I016, fine: extended_float_f64(), coarse: arb_extended_i16(), cases: 1024, }
}