#[derive(Clone, Copy, PartialEq, Eq)]
pub(crate) struct Fpr(pub(crate) u64);
pub(crate) const FPR_ZERO: Fpr = Fpr(0);
impl Fpr {
#[inline]
pub(crate) const fn from_f64(x: f64) -> Fpr {
Fpr(x.to_bits())
}
#[cfg(test)]
#[inline]
pub(crate) const fn to_f64(self) -> f64 {
f64::from_bits(self.0)
}
#[cfg(test)]
#[inline]
pub(crate) fn is_zero(self) -> bool {
(self.0 & 0x7FFF_FFFF_FFFF_FFFF) == 0
}
#[inline]
pub(crate) fn neg(self) -> Fpr {
Fpr(self.0 ^ 0x8000_0000_0000_0000)
}
#[cfg(test)]
#[inline]
pub(crate) fn abs(self) -> Fpr {
Fpr(self.0 & 0x7FFF_FFFF_FFFF_FFFF)
}
}
#[inline]
fn decode(x: Fpr) -> (u64, i32, u128) {
let bits = x.0;
let sign = bits >> 63;
let eb = ((bits >> 52) & 0x7FF) as i32;
let frac = bits & 0x000F_FFFF_FFFF_FFFF;
if eb == 0 {
(sign, -1074, frac as u128)
} else {
(sign, eb - 1075, (frac | 0x0010_0000_0000_0000) as u128)
}
}
fn pack(sign: u64, e: i32, m: u128) -> Fpr {
if m == 0 {
return Fpr(sign << 63); }
let l = 128 - m.leading_zeros() as i32; let mm: u64;
let big_e: i32;
if l > 53 {
let drop = l - 53;
let sig = (m >> drop) as u64; let round = ((m >> (drop - 1)) & 1) as u64;
let sticky = ((m & ((1u128 << (drop - 1)) - 1)) != 0) as u64;
mm = (sig << 2) | (round << 1) | sticky;
big_e = e + drop - 2;
} else {
let up = 53 - l;
mm = ((m as u64) << up) << 2; big_e = e - up - 2;
}
let mut eb = big_e + 1077;
let mut sig = mm >> 2; let round = (mm >> 1) & 1;
let sticky = mm & 1;
if eb >= 2047 {
return Fpr((sign << 63) | (0x7FFu64 << 52)); }
if eb >= 1 {
if round == 1 && (sticky == 1 || (sig & 1) == 1) {
sig += 1;
if sig == (1u64 << 53) {
sig >>= 1;
eb += 1;
if eb >= 2047 {
return Fpr((sign << 63) | (0x7FFu64 << 52));
}
}
}
return Fpr((sign << 63) | ((eb as u64) << 52) | (sig & 0x000F_FFFF_FFFF_FFFF));
}
let sh = 1 - eb; let fb = 2 + sh; if fb >= 64 {
return Fpr(sign << 63);
}
let fb = fb as u32;
let frac = mm >> fb;
let rbit = (mm >> (fb - 1)) & 1;
let st = (mm & ((1u64 << (fb - 1)) - 1)) != 0;
let mut f = frac;
if rbit == 1 && (st || (f & 1) == 1) {
f += 1;
}
if f == (1u64 << 52) {
return Fpr((sign << 63) | (1u64 << 52));
}
Fpr((sign << 63) | (f & 0x000F_FFFF_FFFF_FFFF))
}
fn udivmod128(num: u128, den: u128) -> (u128, u128) {
let mut quo: u128 = 0;
let mut rem: u128 = 0;
let mut i: i32 = 127;
while i >= 0 {
rem = (rem << 1) | ((num >> i) & 1);
let ge = (rem >= den) as u128;
let mask = ge.wrapping_neg();
rem -= den & mask;
quo |= ge << i;
i -= 1;
}
(quo, rem)
}
fn isqrt128(n: u128) -> u128 {
let mut num = n;
let mut res: u128 = 0;
let mut bit: u128 = 1u128 << 126;
let mut k = 0;
while k < 64 {
let t = res + bit;
let ge = (num >= t) as u128;
let mask = ge.wrapping_neg();
num -= t & mask;
res = (res >> 1) + (bit & mask);
bit >>= 2;
k += 1;
}
res
}
impl Fpr {
#[inline]
pub(crate) fn of_i64(i: i64) -> Fpr {
let sign = (i < 0) as u64;
let mag = (i as i128).unsigned_abs();
pack(sign, 0, mag)
}
pub(crate) fn add(self, other: Fpr) -> Fpr {
let (sa, ea, ma) = decode(self);
let (sb, eb, mb) = decode(other);
if ma == 0 && mb == 0 {
return Fpr((sa & sb) << 63);
}
if ma == 0 {
return other;
}
if mb == 0 {
return self;
}
let va = ea + (127 - ma.leading_zeros() as i32);
let vb = eb + (127 - mb.leading_zeros() as i32);
let (sa, ea, ma, sb, eb, mb) = if va >= vb {
(sa, ea, ma, sb, eb, mb)
} else {
(sb, eb, mb, sa, ea, ma)
};
let la = 128 - ma.leading_zeros() as i32;
let shift_a = 122 - la; let acc_a = ma << shift_a;
let efr = ea - shift_a; let shift_b = eb - efr; let (acc_b, sticky) = if shift_b >= 0 {
(mb << shift_b, 0u128)
} else {
let s = (-shift_b) as u32;
if s >= 128 {
(0u128, (mb != 0) as u128)
} else {
let lost = mb & ((1u128 << s) - 1);
(mb >> s, (lost != 0) as u128)
}
};
if sa == sb {
let acc = (acc_a + acc_b) | sticky;
pack(sa, efr, acc)
} else {
let acc_b_eff = acc_b;
if acc_a == acc_b_eff && sticky == 0 {
return FPR_ZERO;
}
if acc_a > acc_b_eff {
let mut acc = acc_a - acc_b_eff;
if sticky != 0 {
acc -= 1;
acc |= 1;
}
pack(sa, efr, acc)
} else if acc_b_eff > acc_a {
let acc = (acc_b_eff - acc_a) | sticky;
pack(sb, efr, acc)
} else {
pack(sb, efr, sticky)
}
}
}
#[inline]
pub(crate) fn sub(self, other: Fpr) -> Fpr {
self.add(other.neg())
}
pub(crate) fn mul(self, other: Fpr) -> Fpr {
let (sa, ea, ma) = decode(self);
let (sb, eb, mb) = decode(other);
let sign = sa ^ sb;
if ma == 0 || mb == 0 {
return Fpr(sign << 63); }
let prod = ma * mb;
pack(sign, ea + eb, prod)
}
pub(crate) fn div(self, other: Fpr) -> Fpr {
let (sa, ea, ma) = decode(self);
let (sb, eb, mb) = decode(other);
let sign = sa ^ sb;
if ma == 0 {
return Fpr(sign << 63); }
if mb == 0 {
return Fpr((sign << 63) | (0x7FFu64 << 52)); }
let num = ma << 64;
let (quo, rem) = udivmod128(num, mb);
let q = quo | ((rem != 0) as u128); pack(sign, ea - eb - 64, q)
}
pub(crate) fn sqrt(self) -> Fpr {
let (s, mut e, m) = decode(self);
if m == 0 {
return Fpr(s << 63); }
if s == 1 {
return Fpr(0x7FF8_0000_0000_0000); }
let mut mm = m;
if (e & 1) != 0 {
mm <<= 1;
e -= 1;
}
let scaled = mm << 56;
let root = isqrt128(scaled);
let sticky = (root * root != scaled) as u128;
pack(0, e / 2 - 28, root | sticky)
}
#[inline]
pub(crate) fn half(self) -> Fpr {
self.mul(Fpr::from_f64(0.5))
}
#[inline]
pub(crate) fn double(self) -> Fpr {
self.add(self)
}
pub(crate) fn rint(self) -> i64 {
let (s, e, m) = decode(self);
if m == 0 {
return 0;
}
let mag: u128 = if e >= 0 {
if e >= 128 {
return if s == 1 { i64::MIN } else { i64::MAX };
}
m << e
} else {
let sh = (-e) as u32;
if sh >= 128 {
0
} else {
let intpart = m >> sh;
let frac = m & ((1u128 << sh) - 1);
let half = 1u128 << (sh - 1);
if frac > half || (frac == half && (intpart & 1) == 1) {
intpart + 1
} else {
intpart
}
}
};
if s == 1 { -(mag as i64) } else { mag as i64 }
}
pub(crate) fn floor(self) -> i64 {
let (s, e, m) = decode(self);
if m == 0 {
return 0;
}
if e >= 0 {
if e >= 128 {
return if s == 1 { i64::MIN } else { i64::MAX };
}
let mag = m << e;
return if s == 1 { -(mag as i64) } else { mag as i64 };
}
let sh = (-e) as u32;
if sh >= 128 {
return if s == 1 { -1 } else { 0 };
}
let intpart = m >> sh;
let has_frac = (m & ((1u128 << sh) - 1)) != 0;
if s == 1 {
-((intpart + has_frac as u128) as i64)
} else {
intpart as i64
}
}
pub(crate) fn trunc(self) -> i64 {
let (s, e, m) = decode(self);
if m == 0 {
return 0;
}
let mag = if e >= 0 {
if e >= 128 {
return if s == 1 { i64::MIN } else { i64::MAX };
}
m << e
} else {
let sh = (-e) as u32;
if sh >= 128 { 0 } else { m >> sh }
};
if s == 1 { -(mag as i64) } else { mag as i64 }
}
#[inline]
fn order_key(self) -> u64 {
let b = self.0;
let mask = (b >> 63).wrapping_neg() | 0x8000_0000_0000_0000;
b ^ mask
}
#[inline]
pub(crate) fn lt(self, other: Fpr) -> bool {
self.order_key() < other.order_key()
}
#[cfg(test)]
#[inline]
pub(crate) fn le(self, other: Fpr) -> bool {
self.order_key() <= other.order_key()
}
}
#[cfg(test)]
#[path = "fpr_tests.rs"]
mod fpr_tests;