use core::{
fmt::Debug,
ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign},
};
use crate::macros::forward_ref_binop;
#[derive(Clone, Copy)]
#[repr(transparent)]
pub struct F64(u64);
impl F64 {
pub const MASK_SIGNIFICAND: u64 = 0x1fffffffffffff;
pub const MASK_EXPONENT: u64 = !Self::MASK_SIGNIFICAND;
pub const NEG_EXPONENT_MAX: i16 = 1024;
pub const NEG_EXPONENT_UNSIGNED_MAX: u16 = 2047;
pub const NEG_EXPONENT_UNSIGNED_ZERO: u16 = 1023;
pub const ZERO: Self = Self(0);
pub const ONE: Self = Self((Self::NEG_EXPONENT_UNSIGNED_ZERO as u64) << 53 | 1);
pub const INFINITY: Self = Self((Self::NEG_EXPONENT_UNSIGNED_MAX as u64) << 53);
pub const NAN: Self = Self((Self::NEG_EXPONENT_UNSIGNED_MAX as u64) << 53 | 1);
#[inline(always)]
pub const fn to_bits(self) -> u64 {
self.0
}
#[inline(always)]
const fn neg_exponent(self) -> i16 {
self.neg_exponent_unsigned() as i16 - Self::NEG_EXPONENT_UNSIGNED_ZERO as i16
}
const fn neg_exponent_unsigned(self) -> u16 {
(self.0 >> 53) as u16
}
#[inline(always)]
pub const fn exponent(self) -> i16 {
-self.neg_exponent()
}
#[inline(always)]
pub const fn significand(self) -> u64 {
self.0 & Self::MASK_SIGNIFICAND
}
const fn neg_exponent_and_significand(self) -> (i16, u64) {
(self.neg_exponent(), self.significand())
}
const fn neg_exponent_unsigned_and_significand(self) -> (u16, u64) {
(self.neg_exponent_unsigned(), self.significand())
}
#[inline(always)]
pub const fn exponent_and_significand(self) -> (i16, u64) {
(self.exponent(), self.significand())
}
#[inline(always)]
pub const fn abs(self) -> f64 {
f64::from_bits((self.0 & Self::MASK_EXPONENT) >> 1 | self.is_nan() as u64)
}
#[inline]
pub fn fract(self) -> f64 {
let x = (self.significand() as f64) / self.abs();
x - libm::trunc(x)
}
#[inline]
pub const fn is_nan(self) -> bool {
self.0 & Self::MASK_EXPONENT == Self::MASK_EXPONENT && self.0 & Self::MASK_SIGNIFICAND != 0
}
#[inline(always)]
pub const fn is_infinite(self) -> bool {
self.0 == Self::INFINITY.0
}
#[inline(always)]
pub const fn is_finite(self) -> bool {
self.0 & Self::MASK_EXPONENT != Self::MASK_EXPONENT
}
pub const fn sqrt(self) -> Self {
let (e, s) = self.neg_exponent_and_significand();
assert!(e % 2 == 0);
assert!(s % 8 == 1);
let two_b = self.significand() >> 2;
let mut x = two_b; let mut two_xp1 = x.wrapping_shl(1).wrapping_add(1);
let mut i = 2;
while i < 6 {
x = x.wrapping_sub(
x.wrapping_mul(x)
.wrapping_add(x)
.wrapping_sub(two_b)
.wrapping_mul(invert(two_xp1, i)),
);
two_xp1 = x.wrapping_shl(1) | 1;
i += 1;
}
Self(
(((e / 2 + Self::NEG_EXPONENT_UNSIGNED_ZERO as i16) as u64) << 53)
| (two_xp1 & Self::MASK_SIGNIFICAND),
)
}
pub const fn recip(self) -> Self {
let (e, s) = self.neg_exponent_unsigned_and_significand();
let exponent = 2046u16.wrapping_sub(e) | (((self.0 == 0) as u16) * Self::NEG_EXPONENT_UNSIGNED_MAX);
Self((exponent as u64) << 53 | invert(s, 5) & Self::MASK_SIGNIFICAND)
}
}
const fn invert(x: u64, num_iterations: u8) -> u64 {
let mut i = 0;
let mut inverse = x;
while i < num_iterations {
inverse = inverse.wrapping_mul(2u64.wrapping_sub(x.wrapping_mul(inverse)));
i += 1;
}
inverse
}
impl const Neg for F64 {
type Output = Self;
#[inline(always)]
fn neg(self) -> Self::Output {
let (e, s) = self.neg_exponent_unsigned_and_significand();
Self(((e as u64) << 53) | (s.wrapping_neg() & Self::MASK_SIGNIFICAND))
}
}
impl const Add for F64 {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
let (e0, s0) = self.neg_exponent_unsigned_and_significand();
let (e1, s1) = rhs.neg_exponent_unsigned_and_significand();
let max_e = e0.max(e1);
let s2 = (s0.wrapping_shl((max_e - e0) as u32))
.wrapping_add(s1.wrapping_shl((max_e - e1) as u32));
let l = s2.trailing_zeros() as u16;
let e2 = max_e.saturating_sub(l)
| ((e0 == Self::NEG_EXPONENT_UNSIGNED_MAX || e1 == Self::NEG_EXPONENT_UNSIGNED_MAX)
as u16
* Self::NEG_EXPONENT_UNSIGNED_MAX);
Self((e2 as u64) << 53 | ((s2 >> l) & Self::MASK_SIGNIFICAND))
}
}
forward_ref_binop!(impl Add, add for F64, F64);
impl AddAssign for F64 {
#[inline]
fn add_assign(&mut self, rhs: Self) {
*self = *self + rhs;
}
}
impl core::iter::Sum for F64 {
#[inline]
fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
iter.fold(F64::ZERO, |a, b| a + b)
}
}
impl<'a> core::iter::Sum<&'a Self> for F64 {
#[inline]
fn sum<I: Iterator<Item = &'a Self>>(iter: I) -> Self {
iter.fold(F64::ZERO, |a, b| a + *b)
}
}
impl const Mul for F64 {
type Output = Self;
fn mul(self, rhs: Self) -> Self::Output {
let (e0, s0) = self.neg_exponent_and_significand();
let (e1, s1) = rhs.neg_exponent_and_significand();
let mut e2 = ((e0 + e1 + Self::NEG_EXPONENT_UNSIGNED_ZERO as i16) as u64)
.min(Self::NEG_EXPONENT_UNSIGNED_MAX as u64);
e2 |= (e0 == Self::NEG_EXPONENT_MAX || e1 == Self::NEG_EXPONENT_MAX) as u64
* Self::NEG_EXPONENT_UNSIGNED_MAX as u64;
let mut s2 = s0.wrapping_mul(s1) & Self::MASK_SIGNIFICAND;
s2 |= self.is_nan() as u64 | rhs.is_nan() as u64;
Self(e2 << 53 | s2)
}
}
forward_ref_binop!(impl Mul, mul for F64, F64);
impl core::iter::Product for F64 {
#[inline]
fn product<I: Iterator<Item = Self>>(iter: I) -> Self {
iter.fold(F64::ZERO, |a, b| a * b)
}
}
impl<'a> core::iter::Product<&'a Self> for F64 {
#[inline]
fn product<I: Iterator<Item = &'a Self>>(iter: I) -> Self {
iter.fold(F64::ZERO, |a, b| a * *b)
}
}
impl MulAssign for F64 {
#[inline(always)]
fn mul_assign(&mut self, rhs: Self) {
*self = *self * rhs;
}
}
impl const Sub for F64 {
type Output = Self;
#[inline(always)]
fn sub(self, rhs: Self) -> Self::Output {
self + (-rhs)
}
}
impl SubAssign for F64 {
#[inline(always)]
fn sub_assign(&mut self, rhs: Self) {
*self = *self - rhs
}
}
impl const Div for F64 {
type Output = Self;
#[inline]
#[allow(clippy::suspicious_arithmetic_impl)] fn div(self, rhs: Self) -> Self::Output {
self * rhs.recip()
}
}
impl DivAssign for F64 {
#[inline(always)]
fn div_assign(&mut self, rhs: Self) {
*self = *self / rhs
}
}
impl const From<u32> for F64 {
#[inline(always)]
fn from(x: u32) -> Self {
let l = x.trailing_zeros() as u16;
Self(((Self::NEG_EXPONENT_UNSIGNED_ZERO - l) as u64) << 53 | (x as u64) >> l)
}
}
impl Debug for F64 {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_tuple("F64")
.field(&self.neg_exponent())
.field(&self.significand())
.finish()
}
}
#[cfg(feature = "rand")]
#[doc(cfg(feature = "rand"))]
impl rand::distributions::Distribution<F64> for rand::distributions::Standard {
#[inline]
fn sample<R: rand::Rng + ?Sized>(&self, rng: &mut R) -> F64 {
let scale = rand_distr::StandardGeometric.sample(rng);
let mut significand: u64 = self.sample(rng);
significand |= 1; F64((F64::NEG_EXPONENT_UNSIGNED_ZERO as u64 - scale) << 53
| (significand & F64::MASK_SIGNIFICAND))
}
}
#[cfg(test)]
mod tests {}