const LIMBS: usize = 34;
const BIAS: i32 = 1074;
#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)]
struct Specials {
nan: bool,
pos_inf: bool,
neg_inf: bool,
}
impl Specials {
#[inline]
fn merge(&mut self, o: &Specials) {
self.nan |= o.nan;
self.pos_inf |= o.pos_inf;
self.neg_inf |= o.neg_inf;
}
#[inline]
fn any(&self) -> bool {
self.nan || self.pos_inf || self.neg_inf
}
}
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct SumF64 {
limbs: [u64; LIMBS],
specials: Specials,
count: u64,
}
impl Default for SumF64 {
fn default() -> Self {
Self::new()
}
}
impl SumF64 {
pub const BYTES: usize = LIMBS * 8 + 1 + 8;
#[inline]
pub const fn new() -> Self {
Self {
limbs: [0; LIMBS],
specials: Specials {
nan: false,
pos_inf: false,
neg_inf: false,
},
count: 0,
}
}
#[inline]
pub const fn count(&self) -> u64 {
self.count
}
pub fn add(&mut self, x: f64) {
self.count = self.count.saturating_add(1);
let bits = x.to_bits();
let neg = bits >> 63 != 0;
let expf = ((bits >> 52) & 0x7ff) as i32;
let frac = bits & ((1u64 << 52) - 1);
if expf == 0x7ff {
if frac != 0 {
self.specials.nan = true;
} else if neg {
self.specials.neg_inf = true;
} else {
self.specials.pos_inf = true;
}
return;
}
let (m, e) = if expf == 0 {
if frac == 0 {
return; }
(frac, 1 - 1075) } else {
(frac | (1u64 << 52), expf - 1075)
};
let pos = (e + BIAS) as usize; let limb = pos >> 6;
let off = pos & 63;
let wide = (m as u128) << off;
let (lo, hi) = (wide as u64, (wide >> 64) as u64);
if neg {
self.sub_wide(limb, lo, hi);
} else {
self.add_wide(limb, lo, hi);
}
}
pub fn merge(&mut self, other: &SumF64) {
let mut carry = 0u64;
for i in 0..LIMBS {
let (a, c1) = self.limbs[i].overflowing_add(other.limbs[i]);
let (b, c2) = a.overflowing_add(carry);
self.limbs[i] = b;
carry = (c1 as u64) + (c2 as u64);
}
self.specials.merge(&other.specials);
self.count = self.count.saturating_add(other.count);
}
pub fn value(&self) -> f64 {
if self.specials.any() {
return match (
self.specials.nan,
self.specials.pos_inf,
self.specials.neg_inf,
) {
(true, _, _) | (_, true, true) => f64::NAN,
(_, true, false) => f64::INFINITY,
(_, false, true) => f64::NEG_INFINITY,
_ => unreachable!(),
};
}
let negative = self.limbs[LIMBS - 1] >> 63 != 0;
let mag = if negative {
negate(&self.limbs)
} else {
self.limbs
};
round_mag(&mag, negative, 52, -1022, 1023)
}
pub fn to_bytes(&self) -> [u8; Self::BYTES] {
let mut out = [0u8; Self::BYTES];
for (i, l) in self.limbs.iter().enumerate() {
out[i * 8..i * 8 + 8].copy_from_slice(&l.to_le_bytes());
}
out[LIMBS * 8] = (self.specials.nan as u8)
| ((self.specials.pos_inf as u8) << 1)
| ((self.specials.neg_inf as u8) << 2);
out[LIMBS * 8 + 1..].copy_from_slice(&self.count.to_le_bytes());
out
}
pub fn from_bytes(b: &[u8; Self::BYTES]) -> Option<Self> {
let mut limbs = [0u64; LIMBS];
for (i, l) in limbs.iter_mut().enumerate() {
let mut le = [0u8; 8];
le.copy_from_slice(&b[i * 8..i * 8 + 8]);
*l = u64::from_le_bytes(le);
}
let f = b[LIMBS * 8];
if f & !0b111 != 0 {
return None;
}
let mut le = [0u8; 8];
le.copy_from_slice(&b[LIMBS * 8 + 1..]);
Some(Self {
limbs,
specials: Specials {
nan: f & 1 != 0,
pos_inf: f & 2 != 0,
neg_inf: f & 4 != 0,
},
count: u64::from_le_bytes(le),
})
}
#[inline]
fn add_wide(&mut self, limb: usize, lo: u64, hi: u64) {
let (v, mut carry) = self.limbs[limb].overflowing_add(lo);
self.limbs[limb] = v;
let mut i = limb + 1;
if i < LIMBS {
let (v, c1) = self.limbs[i].overflowing_add(hi);
let (v, c2) = v.overflowing_add(carry as u64);
self.limbs[i] = v;
carry = c1 || c2;
i += 1;
}
while carry && i < LIMBS {
let (v, c) = self.limbs[i].overflowing_add(1);
self.limbs[i] = v;
carry = c;
i += 1;
}
}
#[inline]
fn sub_wide(&mut self, limb: usize, lo: u64, hi: u64) {
let (v, mut borrow) = self.limbs[limb].overflowing_sub(lo);
self.limbs[limb] = v;
let mut i = limb + 1;
if i < LIMBS {
let (v, b1) = self.limbs[i].overflowing_sub(hi);
let (v, b2) = v.overflowing_sub(borrow as u64);
self.limbs[i] = v;
borrow = b1 || b2;
i += 1;
}
while borrow && i < LIMBS {
let (v, b) = self.limbs[i].overflowing_sub(1);
self.limbs[i] = v;
borrow = b;
i += 1;
}
}
}
impl core::iter::FromIterator<f64> for SumF64 {
fn from_iter<I: IntoIterator<Item = f64>>(iter: I) -> Self {
let mut acc = Self::new();
for x in iter {
acc.add(x);
}
acc
}
}
impl core::iter::Extend<f64> for SumF64 {
fn extend<I: IntoIterator<Item = f64>>(&mut self, iter: I) {
for x in iter {
self.add(x);
}
}
}
#[derive(Clone, PartialEq, Eq, Debug, Default)]
pub struct SumF32 {
inner: SumF64,
}
impl SumF32 {
pub const BYTES: usize = SumF64::BYTES;
#[inline]
pub const fn new() -> Self {
Self {
inner: SumF64::new(),
}
}
#[inline]
pub const fn count(&self) -> u64 {
self.inner.count()
}
#[inline]
pub fn add(&mut self, x: f32) {
self.inner.add(x as f64);
}
#[inline]
pub fn merge(&mut self, other: &SumF32) {
self.inner.merge(&other.inner);
}
pub fn value(&self) -> f32 {
let s = &self.inner;
if s.specials.any() {
return match (s.specials.nan, s.specials.pos_inf, s.specials.neg_inf) {
(true, _, _) | (_, true, true) => f32::NAN,
(_, true, false) => f32::INFINITY,
(_, false, true) => f32::NEG_INFINITY,
_ => unreachable!(),
};
}
let negative = s.limbs[LIMBS - 1] >> 63 != 0;
let mag = if negative { negate(&s.limbs) } else { s.limbs };
round_mag(&mag, negative, 23, -126, 127) as f32
}
#[inline]
pub fn to_bytes(&self) -> [u8; Self::BYTES] {
self.inner.to_bytes()
}
#[inline]
pub fn from_bytes(b: &[u8; Self::BYTES]) -> Option<Self> {
SumF64::from_bytes(b).map(|inner| Self { inner })
}
}
impl core::iter::FromIterator<f32> for SumF32 {
fn from_iter<I: IntoIterator<Item = f32>>(iter: I) -> Self {
let mut acc = Self::new();
for x in iter {
acc.add(x);
}
acc
}
}
#[cfg(feature = "serde")]
mod serde_impls {
use super::{SumF32, SumF64};
use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
impl Serialize for SumF64 {
fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
s.serialize_bytes(&self.to_bytes())
}
}
struct BytesVisitor;
impl<'de> de::Visitor<'de> for BytesVisitor {
type Value = SumF64;
fn expecting(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{} canonical bitrep accumulator bytes", SumF64::BYTES)
}
fn visit_bytes<E: de::Error>(self, v: &[u8]) -> Result<SumF64, E> {
let arr: &[u8; SumF64::BYTES] = v
.try_into()
.map_err(|_| E::invalid_length(v.len(), &self))?;
SumF64::from_bytes(arr).ok_or_else(|| E::custom("invalid bitrep flag byte"))
}
fn visit_seq<A: de::SeqAccess<'de>>(self, mut seq: A) -> Result<SumF64, A::Error> {
let mut arr = [0u8; SumF64::BYTES];
for (i, slot) in arr.iter_mut().enumerate() {
*slot = seq
.next_element()?
.ok_or_else(|| de::Error::invalid_length(i, &self))?;
}
if seq.next_element::<u8>()?.is_some() {
return Err(de::Error::invalid_length(SumF64::BYTES + 1, &self));
}
SumF64::from_bytes(&arr).ok_or_else(|| de::Error::custom("invalid bitrep flag byte"))
}
}
impl<'de> Deserialize<'de> for SumF64 {
fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
d.deserialize_bytes(BytesVisitor)
}
}
impl Serialize for SumF32 {
fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
s.serialize_bytes(&self.to_bytes())
}
}
impl<'de> Deserialize<'de> for SumF32 {
fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
let inner = SumF64::deserialize(d)?;
Ok(SumF32 { inner })
}
}
}
fn negate(l: &[u64; LIMBS]) -> [u64; LIMBS] {
let mut out = [0u64; LIMBS];
let mut carry = 1u64;
for i in 0..LIMBS {
let (v, c) = (!l[i]).overflowing_add(carry);
out[i] = v;
carry = c as u64;
}
out
}
#[inline]
fn bit(mag: &[u64; LIMBS], i: usize) -> bool {
(mag[i >> 6] >> (i & 63)) & 1 != 0
}
fn highest_bit(mag: &[u64; LIMBS]) -> Option<usize> {
for i in (0..LIMBS).rev() {
if mag[i] != 0 {
return Some(i * 64 + 63 - mag[i].leading_zeros() as usize);
}
}
None
}
fn extract(mag: &[u64; LIMBS], shift: usize, nbits: u32) -> u64 {
let limb = shift >> 6;
let off = (shift & 63) as u32;
let lo = mag[limb] >> off;
let hi = if off == 0 || limb + 1 >= LIMBS {
0
} else {
mag[limb + 1] << (64 - off)
};
let v = lo | hi;
if nbits == 64 {
v
} else {
v & ((1u64 << nbits) - 1)
}
}
fn any_below(mag: &[u64; LIMBS], end: usize) -> bool {
let limb = end >> 6;
let off = end & 63;
for l in mag.iter().take(limb) {
if *l != 0 {
return true;
}
}
if off > 0 && limb < LIMBS && mag[limb] & ((1u64 << off) - 1) != 0 {
return true;
}
false
}
fn round_mag(mag: &[u64; LIMBS], negative: bool, mant: u32, min_exp: i32, max_exp: i32) -> f64 {
let h = match highest_bit(mag) {
None => return 0.0, Some(h) => h as i32,
};
let e = h - BIAS;
let sub_top = min_exp + BIAS - 1; let (q, exp) = if h <= sub_top {
let grid = (min_exp - mant as i32 + BIAS) as usize; (round_at(mag, grid, h as usize), min_exp)
} else {
let grid = (h - mant as i32) as usize;
(round_at(mag, grid, h as usize), e)
};
let (q, exp) = if q >> (mant + 1) != 0 {
(q >> 1, exp + 1)
} else {
(q, exp)
};
if exp > max_exp {
return if negative {
f64::NEG_INFINITY
} else {
f64::INFINITY
};
}
let sig = q as f64; let v = sig * pow2(exp - mant as i32);
if negative {
-v
} else {
v
}
}
fn round_at(mag: &[u64; LIMBS], grid: usize, h: usize) -> u64 {
let width = if h >= grid { (h - grid + 1) as u32 } else { 0 };
debug_assert!(
width <= 54,
"significand extraction width {width} exceeds 54 bits"
);
let q = if width == 0 {
0
} else {
extract(mag, grid, width)
};
if grid == 0 {
return q; }
let round_bit = bit(mag, grid - 1);
if !round_bit {
return q;
}
let sticky = any_below(mag, grid - 1);
if sticky || q & 1 == 1 {
q + 1
} else {
q
}
}
fn pow2(k: i32) -> f64 {
debug_assert!((-1074..=1023).contains(&k));
if k >= -1022 {
f64::from_bits(((k + 1023) as u64) << 52)
} else {
f64::from_bits(1u64 << (k + 1074))
}
}