const TWO_COMPLEMENT_64: u64 = 0x8000_0000_0000_0000_u64;
const TWO_COMPLEMENT_CI_64: i64 = TWO_COMPLEMENT_64 as i64;
#[doc(hidden)]
#[inline]
#[must_use]
pub fn almost_equal_as_int64(a: f64, b: f64, ulps: u64) -> bool {
debug_assert!(a.is_finite());
debug_assert!(b.is_finite());
let mut a_i: i64 = a.to_bits() as i64;
let mut b_i: i64 = b.to_bits() as i64;
if a_i < 0i64 {
a_i = TWO_COMPLEMENT_CI_64 - a_i;
}
if b_i < 0i64 {
b_i = TWO_COMPLEMENT_CI_64 - b_i;
}
let diff = (a_i as i128) - (b_i as i128);
diff.abs() <= ulps as i128
}
pub const FLOAT_INT_EPS_64: u64 = 10;
#[doc(hidden)]
#[must_use]
pub fn float_perturbed_as_int64(f: f64, c: i64) -> f64 {
debug_assert!(f.is_finite());
if c == 0 {
return f;
}
if f == 0.0 && c == -1 {
return -0.0;
}
if f == -0.0 && c == 1 {
return 0.0;
}
let f_bits = f.to_bits();
let f_i = f_bits as i64;
let lex_value = if f_i < 0 {
TWO_COMPLEMENT_CI_64 - f_i
} else {
f_i
};
let result_lex = lex_value + c;
let result_bits = if result_lex < 0 {
(TWO_COMPLEMENT_CI_64 - result_lex) as u64
} else {
result_lex as u64
};
f64::from_bits(result_bits)
}
#[must_use]
pub fn float_prev_64(f: f64) -> f64 {
let eps = -(FLOAT_INT_EPS_64 as i64) - 1;
float_perturbed_as_int64(f, eps)
}
#[must_use]
pub fn float_next_64(f: f64) -> f64 {
let eps = FLOAT_INT_EPS_64 as i64 + 1;
float_perturbed_as_int64(f, eps)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_almost_equal_as_int64_basic() {
assert!(almost_equal_as_int64(1.0, 1.0, 0));
let a = 1.0f64;
let b = 1.0000000000000002f64; assert!(almost_equal_as_int64(a, b, 10));
let c = 1.1f64;
assert!(!almost_equal_as_int64(a, c, 10));
}
#[test]
fn test_float_next_prev_64() {
let f = 2.5f64;
let next = float_next_64(f);
let prev = float_prev_64(f);
assert!(next > f);
assert!(prev < f);
assert!(f != next);
assert!(f != prev);
}
#[test]
fn test_float_perturbed_as_int64() {
let f = 3.14159f64;
assert_eq!(float_perturbed_as_int64(f, 0), f);
let perturbed_pos = float_perturbed_as_int64(f, 5);
assert!(perturbed_pos > f);
let perturbed_neg = float_perturbed_as_int64(f, -5);
assert!(perturbed_neg < f);
}
#[test]
fn test_zero_crossing_64() {
assert_eq!(float_perturbed_as_int64(0.0, -1), -0.0);
assert_eq!(float_perturbed_as_int64(-0.0, 1), 0.0);
}
}