pub(crate) const FRACTION_BITS: u32 = 112;
pub(crate) const EXPONENT_BITS: u32 = 15;
pub(crate) const EXPONENT_BIAS: i32 = 16383;
pub(crate) const EXPONENT_MAX: u32 = (1 << EXPONENT_BITS) - 1;
pub(crate) const FRACTION_MASK: u128 = (1u128 << FRACTION_BITS) - 1;
pub(crate) const SIGN_MASK: u128 = 1u128 << (FRACTION_BITS + EXPONENT_BITS);
pub(crate) const EXPONENT_SHIFT: u32 = FRACTION_BITS;
pub(crate) const SIGNIFICAND_BITS: u32 = FRACTION_BITS + 1;
pub(crate) const QUIET_BIT: u128 = 1u128 << (FRACTION_BITS - 1);
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Class {
Nan,
Infinite,
Normal,
Subnormal,
Zero,
}
#[derive(Debug, Clone, Copy, Default)]
pub struct Float128 {
bits: u128,
}
impl Float128 {
pub const ZERO: Self = Self { bits: 0 };
pub const NEG_ZERO: Self = Self { bits: SIGN_MASK };
pub const ONE: Self = Self {
bits: (EXPONENT_BIAS as u128) << EXPONENT_SHIFT,
};
pub const NEG_ONE: Self = Self {
bits: SIGN_MASK | ((EXPONENT_BIAS as u128) << EXPONENT_SHIFT),
};
pub const INFINITY: Self = Self {
bits: (EXPONENT_MAX as u128) << EXPONENT_SHIFT,
};
pub const NEG_INFINITY: Self = Self {
bits: SIGN_MASK | ((EXPONENT_MAX as u128) << EXPONENT_SHIFT),
};
pub const NAN: Self = Self {
bits: ((EXPONENT_MAX as u128) << EXPONENT_SHIFT) | QUIET_BIT,
};
pub const MAX: Self = Self {
bits: (((EXPONENT_MAX - 1) as u128) << EXPONENT_SHIFT) | FRACTION_MASK,
};
pub const MIN: Self = Self {
bits: SIGN_MASK | (((EXPONENT_MAX - 1) as u128) << EXPONENT_SHIFT) | FRACTION_MASK,
};
pub const MIN_POSITIVE: Self = Self {
bits: 1u128 << EXPONENT_SHIFT,
};
pub const MIN_SUBNORMAL: Self = Self { bits: 1 };
pub const EPSILON: Self = Self {
bits: ((EXPONENT_BIAS - FRACTION_BITS as i32) as u128) << EXPONENT_SHIFT,
};
pub const MANTISSA_DIGITS: u32 = SIGNIFICAND_BITS;
pub const DIGITS_ROUND_TRIP: usize = 36;
#[must_use]
pub const fn from_bits(bits: u128) -> Self {
Self { bits }
}
#[must_use]
pub const fn to_bits(self) -> u128 {
self.bits
}
#[must_use]
pub const fn from_be_bytes(bytes: [u8; 16]) -> Self {
Self {
bits: u128::from_be_bytes(bytes),
}
}
#[must_use]
pub const fn to_be_bytes(self) -> [u8; 16] {
self.bits.to_be_bytes()
}
#[must_use]
pub const fn from_le_bytes(bytes: [u8; 16]) -> Self {
Self {
bits: u128::from_le_bytes(bytes),
}
}
#[must_use]
pub const fn to_le_bytes(self) -> [u8; 16] {
self.bits.to_le_bytes()
}
#[must_use]
pub const fn is_sign_negative(self) -> bool {
self.bits & SIGN_MASK != 0
}
#[must_use]
pub const fn is_sign_positive(self) -> bool {
!self.is_sign_negative()
}
#[must_use]
pub(crate) const fn biased_exponent(self) -> u32 {
((self.bits >> EXPONENT_SHIFT) & (EXPONENT_MAX as u128)) as u32
}
#[must_use]
pub(crate) const fn fraction(self) -> u128 {
self.bits & FRACTION_MASK
}
#[must_use]
pub const fn classify(self) -> Class {
let exponent = self.biased_exponent();
let fraction = self.fraction();
if exponent == EXPONENT_MAX {
if fraction == 0 {
Class::Infinite
} else {
Class::Nan
}
} else if exponent == 0 {
if fraction == 0 {
Class::Zero
} else {
Class::Subnormal
}
} else {
Class::Normal
}
}
#[must_use]
pub const fn is_nan(self) -> bool {
matches!(self.classify(), Class::Nan)
}
#[must_use]
pub const fn is_infinite(self) -> bool {
matches!(self.classify(), Class::Infinite)
}
#[must_use]
pub const fn is_finite(self) -> bool {
self.biased_exponent() != EXPONENT_MAX
}
#[must_use]
pub const fn is_zero(self) -> bool {
self.bits & !SIGN_MASK == 0
}
#[must_use]
pub const fn is_subnormal(self) -> bool {
matches!(self.classify(), Class::Subnormal)
}
#[must_use]
pub const fn is_normal(self) -> bool {
matches!(self.classify(), Class::Normal)
}
#[must_use]
pub const fn is_quiet_nan(self) -> bool {
self.is_nan() && (self.bits & QUIET_BIT) != 0
}
#[must_use]
pub const fn abs(self) -> Self {
Self {
bits: self.bits & !SIGN_MASK,
}
}
#[must_use]
pub const fn neg(self) -> Self {
Self {
bits: self.bits ^ SIGN_MASK,
}
}
#[must_use]
pub const fn copysign(self, sign: Self) -> Self {
Self {
bits: (self.bits & !SIGN_MASK) | (sign.bits & SIGN_MASK),
}
}
#[must_use]
pub const fn decompose(self) -> Option<(bool, u128, i32)> {
if !self.is_finite() {
return None;
}
let biased = self.biased_exponent();
let fraction = self.fraction();
let (significand, exponent) = if biased == 0 {
(fraction, 1 - EXPONENT_BIAS - FRACTION_BITS as i32)
} else {
(
fraction | (1u128 << FRACTION_BITS),
biased as i32 - EXPONENT_BIAS - FRACTION_BITS as i32,
)
};
Some((self.is_sign_negative(), significand, exponent))
}
pub(crate) fn from_parts_rounded(
negative: bool,
significand: u128,
exponent: i32,
) -> (Self, bool) {
if significand == 0 {
return (if negative { Self::NEG_ZERO } else { Self::ZERO }, false);
}
const OVERFLOW: i32 = EXPONENT_MAX as i32;
const UNDERFLOW: i32 = -(SIGNIFICAND_BITS as i32) - EXPONENT_BIAS - 8;
if exponent > OVERFLOW {
let value = if negative {
Self::NEG_INFINITY
} else {
Self::INFINITY
};
return (value, true);
}
if exponent < UNDERFLOW - 128 {
let value = if negative { Self::NEG_ZERO } else { Self::ZERO };
return (value, true);
}
let leading = 127 - significand.leading_zeros();
let mut exponent = exponent;
let mut significand = significand;
let mut sticky = false;
if leading > FRACTION_BITS {
let drop = leading - FRACTION_BITS;
let (rounded, lost) = round_shift_right(significand, drop);
significand = rounded;
sticky = lost;
exponent += drop as i32;
if significand > (FRACTION_MASK | (1u128 << FRACTION_BITS)) {
significand >>= 1;
exponent += 1;
}
} else {
let shift = FRACTION_BITS - leading;
significand <<= shift;
exponent -= shift as i32;
}
let biased = exponent + EXPONENT_BIAS + FRACTION_BITS as i32;
if biased >= EXPONENT_MAX as i32 {
let value = if negative {
Self::NEG_INFINITY
} else {
Self::INFINITY
};
return (value, true);
}
if biased <= 0 {
let drop = 1 + biased.unsigned_abs();
let (rounded, lost) = round_shift_right(significand, drop);
sticky |= lost;
let bits = (u128::from(negative) << (FRACTION_BITS + EXPONENT_BITS)) | rounded;
return (Self { bits }, sticky);
}
let bits = (u128::from(negative) << (FRACTION_BITS + EXPONENT_BITS))
| ((biased as u128) << EXPONENT_SHIFT)
| (significand & FRACTION_MASK);
(Self { bits }, sticky)
}
}
pub(crate) fn round_shift_right(value: u128, shift: u32) -> (u128, bool) {
if shift == 0 {
return (value, false);
}
if shift >= 128 {
return (0, value != 0);
}
let kept = value >> shift;
let dropped = value & ((1u128 << shift) - 1);
if dropped == 0 {
return (kept, false);
}
let half = 1u128 << (shift - 1);
let rounded = if dropped > half || (dropped == half && kept & 1 == 1) {
kept + 1
} else {
kept
};
(rounded, true)
}
impl core::ops::Neg for Float128 {
type Output = Self;
fn neg(self) -> Self {
Self::neg(self)
}
}