pub(super) fn f64_trunc(x: f64) -> f64 {
if x.is_nan() || x.is_infinite() {
return x;
}
if x >= 9_007_199_254_740_992.0 || x <= -9_007_199_254_740_992.0 {
return x;
}
(x as i64) as f64
}
static PRNG_STATE: core::sync::atomic::AtomicU64 =
core::sync::atomic::AtomicU64::new(0x2545_F491_4F6C_DD1D);
pub(super) fn prng_next_u64() -> u64 {
use core::sync::atomic::Ordering;
let mut x = PRNG_STATE.load(Ordering::Relaxed);
loop {
if x == 0 {
x = 0x2545_F491_4F6C_DD1D;
}
let mut next = x;
next ^= next << 13;
next ^= next >> 7;
next ^= next << 17;
match PRNG_STATE.compare_exchange_weak(x, next, Ordering::Relaxed, Ordering::Relaxed) {
Ok(_) => return next,
Err(seen) => x = seen,
}
}
}
pub(super) fn prng_next_f64() -> f64 {
let mantissa = prng_next_u64() >> 11;
let denom = (1u64 << 53) as f64;
mantissa as f64 / denom
}
pub(crate) fn f64_sqrt(x: f64) -> f64 {
if x == 0.0 || x.is_nan() {
return x;
}
if x.is_infinite() {
return x;
}
let bits = x.to_bits();
let exp = ((bits >> 52) & 0x7ff) as i64 - 1023;
let new_exp = (exp / 2) + 1023;
let mut guess = f64::from_bits(((new_exp as u64) & 0x7ff) << 52);
for _ in 0..8 {
guess = 0.5 * (guess + x / guess);
}
guess
}
pub(super) fn f64_exp(x: f64) -> f64 {
if x.is_nan() {
return x;
}
if x > 709.0 {
return f64::INFINITY;
}
if x < -745.0 {
return 0.0;
}
const LN2: f64 = 0.6931471805599453;
let k = f64_round_half_away(x / LN2) as i32;
let r = x - (k as f64) * LN2;
let mut term = 1.0;
let mut sum = 1.0;
for n in 1..=20 {
term *= r / (n as f64);
sum += term;
if term.abs() < 1e-18 {
break;
}
}
f64_powi(2.0, k) * sum
}
pub(super) fn f64_ln(x: f64) -> f64 {
if x <= 0.0 {
return f64::NAN;
}
if x == 1.0 {
return 0.0;
}
const LN2: f64 = 0.6931471805599453;
let mut k = 0i32;
let mut m = x;
while m >= 2.0 {
m *= 0.5;
k += 1;
}
while m < 1.0 {
m *= 2.0;
k -= 1;
}
let u = (m - 1.0) / (m + 1.0);
let u2 = u * u;
let mut term = u;
let mut sum = u;
for k_iter in 1..50 {
term *= u2;
let denom = (2 * k_iter + 1) as f64;
sum += term / denom;
if (term / denom).abs() < 1e-18 {
break;
}
}
2.0 * sum + (k as f64) * LN2
}
pub(super) fn f64_powi(base: f64, exp: i32) -> f64 {
if exp == 0 {
return 1.0;
}
let mut result = 1.0;
let mut b = if exp > 0 { base } else { 1.0 / base };
let mut e = exp.unsigned_abs();
while e > 0 {
if e & 1 == 1 {
result *= b;
}
e >>= 1;
if e > 0 {
b *= b;
}
}
result
}
pub(super) fn f64_round_half_away(x: f64) -> f64 {
if x.is_nan() || x.is_infinite() {
return x;
}
if x >= 0.0 {
f64_floor(x + 0.5)
} else {
f64_ceil(x - 0.5)
}
}
pub(crate) fn f64_ceil(x: f64) -> f64 {
if x.is_nan() || x.is_infinite() {
return x;
}
if x >= 9_007_199_254_740_992.0 || x <= -9_007_199_254_740_992.0 {
return x;
}
let trunc = (x as i64) as f64;
if x > 0.0 && x != trunc {
trunc + 1.0
} else {
trunc
}
}
pub(crate) fn f64_floor(x: f64) -> f64 {
if x.is_nan() || x.is_infinite() {
return x;
}
if x >= 9_007_199_254_740_992.0 || x <= -9_007_199_254_740_992.0 {
return x;
}
let trunc = (x as i64) as f64;
if x < 0.0 && x != trunc {
trunc - 1.0
} else {
trunc
}
}