use core::fmt;
use crate::float::{
EXPONENT_BIAS, EXPONENT_MAX, EXPONENT_SHIFT, FRACTION_BITS, Float128, QUIET_BIT, SIGN_MASK,
};
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Inexact<T> {
pub rounded: T,
pub source: Float128,
}
impl<T> fmt::Display for Inexact<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"the binary128 value 0x{:032x} does not fit the destination type exactly",
self.source.to_bits()
)
}
}
impl<T: fmt::Debug> core::error::Error for Inexact<T> {}
impl Float128 {
#[must_use]
pub fn from_f64(value: f64) -> Self {
let bits = value.to_bits();
let negative = bits >> 63 != 0;
let biased = ((bits >> 52) & 0x7ff) as u32;
let fraction = u128::from(bits & 0x000f_ffff_ffff_ffff);
widen(negative, biased, fraction, 11, 52)
}
#[must_use]
pub fn from_f32(value: f32) -> Self {
let bits = value.to_bits();
let negative = bits >> 31 != 0;
let biased = (bits >> 23) & 0xff;
let fraction = u128::from(bits & 0x007f_ffff);
widen(negative, biased, fraction, 8, 23)
}
#[must_use]
pub fn from_i128_rounded(value: i128) -> Self {
let negative = value < 0;
let magnitude = value.unsigned_abs();
Self::from_parts_rounded(negative, magnitude, 0).0
}
#[must_use]
pub fn from_u128_rounded(value: u128) -> Self {
Self::from_parts_rounded(false, value, 0).0
}
pub fn try_from_i128(value: i128) -> Result<Self, Inexact<Self>> {
let negative = value < 0;
let (rounded, lost) = Self::from_parts_rounded(negative, value.unsigned_abs(), 0);
if lost {
Err(Inexact {
rounded,
source: rounded,
})
} else {
Ok(rounded)
}
}
pub fn try_from_u128(value: u128) -> Result<Self, Inexact<Self>> {
let (rounded, lost) = Self::from_parts_rounded(false, value, 0);
if lost {
Err(Inexact {
rounded,
source: rounded,
})
} else {
Ok(rounded)
}
}
}
fn widen(
negative: bool,
biased: u32,
fraction: u128,
exponent_bits: u32,
fraction_bits: u32,
) -> Float128 {
let sign = u128::from(negative) << (FRACTION_BITS + 15);
let source_max = (1u32 << exponent_bits) - 1;
let source_bias = (1i32 << (exponent_bits - 1)) - 1;
let shift = FRACTION_BITS - fraction_bits;
if biased == source_max {
let bits = sign | ((EXPONENT_MAX as u128) << EXPONENT_SHIFT) | (fraction << shift);
return Float128::from_bits(bits);
}
if biased == 0 {
if fraction == 0 {
return Float128::from_bits(sign);
}
let exponent = 1 - source_bias - fraction_bits as i32;
let (value, lost) = Float128::from_parts_rounded(negative, fraction, exponent);
debug_assert!(!lost, "widening a narrower binary format is always exact");
return value;
}
let biased128 = biased as i32 - source_bias + EXPONENT_BIAS;
let bits = sign | ((biased128 as u128) << EXPONENT_SHIFT) | (fraction << shift);
Float128::from_bits(bits)
}
impl Float128 {
#[must_use]
pub fn to_f64_rounded(self) -> f64 {
self.narrow_f64().0
}
pub fn to_f64(self) -> Result<f64, Inexact<f64>> {
let (rounded, lost) = self.narrow_f64();
if lost {
Err(Inexact {
rounded,
source: self,
})
} else {
Ok(rounded)
}
}
#[must_use]
pub fn to_f32_rounded(self) -> f32 {
self.narrow_f32().0
}
pub fn to_f32(self) -> Result<f32, Inexact<f32>> {
let (rounded, lost) = self.narrow_f32();
if lost {
Err(Inexact {
rounded,
source: self,
})
} else {
Ok(rounded)
}
}
fn narrow_f64(self) -> (f64, bool) {
let (bits, lost) = self.narrow(11, 52);
(f64::from_bits(bits as u64), lost)
}
fn narrow_f32(self) -> (f32, bool) {
let (bits, lost) = self.narrow(8, 23);
(f32::from_bits(bits as u32), lost)
}
fn narrow(self, exponent_bits: u32, fraction_bits: u32) -> (u128, bool) {
let target_max = (1u32 << exponent_bits) - 1;
let target_bias = (1i32 << (exponent_bits - 1)) - 1;
let sign = u128::from(self.is_sign_negative()) << (exponent_bits + fraction_bits);
let shift = FRACTION_BITS - fraction_bits;
if self.is_nan() {
let mut payload = self.fraction() >> shift;
if payload == 0 {
payload = 1;
}
let bits = sign | (u128::from(target_max) << fraction_bits) | payload;
return (bits, self.fraction() & ((1u128 << shift) - 1) != 0);
}
if self.is_infinite() {
return (sign | (u128::from(target_max) << fraction_bits), false);
}
let Some((_, significand, exponent)) = self.decompose() else {
return (sign, false);
};
if significand == 0 {
return (sign, false);
}
let leading = 127 - significand.leading_zeros();
let mut exponent = exponent + leading as i32;
let target_exponent = exponent + target_bias;
if target_exponent >= target_max as i32 {
let bits = sign | (u128::from(target_max) << fraction_bits);
return (bits, true);
}
let lost;
let mut fraction;
if target_exponent <= 0 {
let drop = leading as i32 - fraction_bits as i32 + (1 - target_exponent);
let drop = drop.max(0).unsigned_abs();
let (rounded, dropped) = crate::float::round_shift_right(significand, drop);
let bits = sign | rounded;
return (bits, dropped);
} else if leading > fraction_bits {
let drop = leading - fraction_bits;
let (rounded, dropped) = crate::float::round_shift_right(significand, drop);
lost = dropped;
fraction = rounded;
if fraction >> fraction_bits > 1 {
fraction >>= 1;
exponent += 1;
}
} else {
fraction = significand << (fraction_bits - leading);
lost = false;
}
let target_exponent = exponent + target_bias;
if target_exponent >= target_max as i32 {
let bits = sign | (u128::from(target_max) << fraction_bits);
return (bits, true);
}
let bits = sign
| ((target_exponent as u128) << fraction_bits)
| (fraction & ((1u128 << fraction_bits) - 1));
(bits, lost)
}
}
impl Float128 {
pub fn to_i128(self) -> Result<i128, Inexact<i128>> {
let (value, exact) = self.trunc_i128();
if exact {
Ok(value)
} else {
Err(Inexact {
rounded: value,
source: self,
})
}
}
pub fn to_i64(self) -> Result<i64, Inexact<i64>> {
match self.to_i128() {
Ok(wide) => i64::try_from(wide).map_err(|_| Inexact {
rounded: if wide < 0 { i64::MIN } else { i64::MAX },
source: self,
}),
Err(inexact) => Err(Inexact {
rounded: i64::try_from(inexact.rounded).unwrap_or(if inexact.rounded < 0 {
i64::MIN
} else {
i64::MAX
}),
source: self,
}),
}
}
fn trunc_i128(self) -> (i128, bool) {
if self.is_nan() {
return (0, false);
}
if self.is_infinite() {
return (
if self.is_sign_negative() {
i128::MIN
} else {
i128::MAX
},
false,
);
}
let Some((negative, significand, exponent)) = self.decompose() else {
return (0, false);
};
if significand == 0 {
return (0, true);
}
let leading = 127 - significand.leading_zeros();
let magnitude_bits = leading as i32 + exponent + 1;
let limit = if negative { 128 } else { 127 };
if magnitude_bits > limit {
return (if negative { i128::MIN } else { i128::MAX }, false);
}
let (magnitude, fractional) = if exponent >= 0 {
let shift = exponent.unsigned_abs();
if shift >= 128 {
return (if negative { i128::MIN } else { i128::MAX }, false);
}
(significand << shift, false)
} else {
let shift = exponent.unsigned_abs();
if shift >= 128 {
(0, true)
} else {
(
significand >> shift,
significand & ((1u128 << shift) - 1) != 0,
)
}
};
let signed = if negative {
magnitude.wrapping_neg() as i128
} else {
magnitude as i128
};
(signed, !fractional)
}
}
impl From<f64> for Float128 {
fn from(value: f64) -> Self {
Self::from_f64(value)
}
}
impl From<f32> for Float128 {
fn from(value: f32) -> Self {
Self::from_f32(value)
}
}
impl TryFrom<Float128> for f64 {
type Error = Inexact<f64>;
fn try_from(value: Float128) -> Result<Self, Self::Error> {
value.to_f64()
}
}
impl TryFrom<Float128> for f32 {
type Error = Inexact<f32>;
fn try_from(value: Float128) -> Result<Self, Self::Error> {
value.to_f32()
}
}
impl Float128 {
#[must_use]
pub const fn quiet_nan(payload: u128) -> Self {
let fraction = (payload & (QUIET_BIT - 1)) | QUIET_BIT;
Self::from_bits(((EXPONENT_MAX as u128) << EXPONENT_SHIFT) | fraction)
}
#[must_use]
pub const fn neg_quiet_nan(payload: u128) -> Self {
Self::from_bits(Self::quiet_nan(payload).to_bits() | SIGN_MASK)
}
}