use crate::float::{
EXPONENT_MAX, EXPONENT_SHIFT, FRACTION_BITS, FRACTION_MASK, Float128, QUIET_BIT,
round_shift_right,
};
const X87_SIGNIFICAND_BITS: u32 = 64;
const X87_FRACTION_BITS: u32 = X87_SIGNIFICAND_BITS - 1;
const X87_BIAS: i32 = 16383;
const X87_EXPONENT_MAX: u32 = 0x7fff;
const X87_QUIET: u64 = 0x4000_0000_0000_0000;
const X87_INTEGER: u64 = 0x8000_0000_0000_0000;
const X87_PAYLOAD_MASK: u64 = X87_QUIET - 1;
const X87_PAYLOAD_SHIFT: u32 = FRACTION_BITS - 1 - 62;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Extended80 {
pub sign_exponent: u16,
pub significand: u64,
}
impl Extended80 {
#[must_use]
pub const fn from_le_bytes(bytes: [u8; 10]) -> Self {
let significand = u64::from_le_bytes([
bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7],
]);
Self {
sign_exponent: u16::from_le_bytes([bytes[8], bytes[9]]),
significand,
}
}
#[must_use]
pub const fn to_le_bytes(self) -> [u8; 10] {
let significand = self.significand.to_le_bytes();
let sign_exponent = self.sign_exponent.to_le_bytes();
[
significand[0],
significand[1],
significand[2],
significand[3],
significand[4],
significand[5],
significand[6],
significand[7],
sign_exponent[0],
sign_exponent[1],
]
}
#[must_use]
pub const fn from_padded_le_bytes(bytes: [u8; 16]) -> Self {
Self::from_le_bytes([
bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7],
bytes[8], bytes[9],
])
}
#[must_use]
pub const fn to_padded_le_bytes(self) -> [u8; 16] {
let ten = self.to_le_bytes();
[
ten[0], ten[1], ten[2], ten[3], ten[4], ten[5], ten[6], ten[7], ten[8], ten[9], 0, 0,
0, 0, 0, 0,
]
}
#[must_use]
pub const fn is_sign_negative(self) -> bool {
self.sign_exponent & 0x8000 != 0
}
#[must_use]
pub const fn biased_exponent(self) -> u32 {
(self.sign_exponent & 0x7fff) as u32
}
}
impl Float128 {
#[must_use]
pub fn from_x87_extended(value: Extended80) -> Self {
let negative = value.is_sign_negative();
let biased = value.biased_exponent();
let significand = value.significand;
if biased == X87_EXPONENT_MAX {
let payload = u128::from(significand & X87_PAYLOAD_MASK);
if payload == 0 && significand & X87_QUIET == 0 {
return if negative {
Self::NEG_INFINITY
} else {
Self::INFINITY
};
}
let quiet = significand & X87_QUIET != 0;
let fraction = (payload << X87_PAYLOAD_SHIFT) | if quiet { QUIET_BIT } else { 0 };
let fraction = if fraction & FRACTION_MASK == 0 {
1
} else {
fraction
};
let bits = (u128::from(negative) << 127)
| ((EXPONENT_MAX as u128) << EXPONENT_SHIFT)
| (fraction & FRACTION_MASK);
return Self::from_bits(bits);
}
if significand == 0 {
return if negative { Self::NEG_ZERO } else { Self::ZERO };
}
let unbiased = if biased == 0 {
1 - X87_BIAS
} else {
biased as i32 - X87_BIAS
};
let (value, lost) = Self::from_parts_rounded(
negative,
u128::from(significand),
unbiased - X87_FRACTION_BITS as i32,
);
debug_assert!(!lost, "64 significand bits always fit inside 113");
value
}
#[must_use]
pub fn from_x87_padded_bytes(bytes: [u8; 16]) -> Self {
Self::from_x87_extended(Extended80::from_padded_le_bytes(bytes))
}
#[must_use]
pub fn to_x87_extended(self) -> (Extended80, bool) {
let negative = self.is_sign_negative();
let sign = if negative { 0x8000u16 } else { 0 };
if self.is_nan() {
let payload = ((self.fraction() & !QUIET_BIT) >> X87_PAYLOAD_SHIFT) as u64;
let quiet = self.to_bits() & QUIET_BIT != 0;
let significand =
X87_INTEGER | if quiet { X87_QUIET } else { 0 } | (payload & X87_PAYLOAD_MASK);
let significand = if significand & !X87_INTEGER == 0 {
significand | 1
} else {
significand
};
let lost = self.fraction() & ((1u128 << X87_PAYLOAD_SHIFT) - 1) != 0;
return (
Extended80 {
sign_exponent: sign | X87_EXPONENT_MAX as u16,
significand,
},
!lost,
);
}
if self.is_infinite() {
return (
Extended80 {
sign_exponent: sign | X87_EXPONENT_MAX as u16,
significand: X87_INTEGER,
},
true,
);
}
let Some((_, significand, exponent)) = self.decompose() else {
return (
Extended80 {
sign_exponent: sign,
significand: 0,
},
true,
);
};
if significand == 0 {
return (
Extended80 {
sign_exponent: sign,
significand: 0,
},
true,
);
}
let leading = 127 - significand.leading_zeros();
let unbiased = exponent + leading as i32;
let biased = unbiased + X87_BIAS;
if biased >= X87_EXPONENT_MAX as i32 {
return (
Extended80 {
sign_exponent: sign | X87_EXPONENT_MAX as u16,
significand: X87_INTEGER,
},
false,
);
}
if biased <= 0 {
let drop = (leading as i32 - X87_FRACTION_BITS as i32 + (1 - biased))
.max(0)
.unsigned_abs();
let (rounded, lost) = round_shift_right(significand, drop);
let extended = Extended80 {
sign_exponent: sign,
significand: (rounded & u128::from(u64::MAX)) as u64,
};
return (extended, !lost);
}
let (fitted, lost) = if leading >= X87_FRACTION_BITS {
round_shift_right(significand, leading - X87_FRACTION_BITS)
} else {
(significand << (X87_FRACTION_BITS - leading), false)
};
let (fitted, biased) = if fitted >> X87_SIGNIFICAND_BITS != 0 {
(fitted >> 1, biased + 1)
} else {
(fitted, biased)
};
if biased >= X87_EXPONENT_MAX as i32 {
return (
Extended80 {
sign_exponent: sign | X87_EXPONENT_MAX as u16,
significand: X87_INTEGER,
},
false,
);
}
let extended = Extended80 {
sign_exponent: sign | ((biased as u32 & X87_EXPONENT_MAX) as u16),
significand: (fitted & u128::from(u64::MAX)) as u64,
};
(extended, !lost)
}
#[must_use]
pub fn to_x87_padded_bytes(self) -> ([u8; 16], bool) {
let (extended, exact) = self.to_x87_extended();
(extended.to_padded_le_bytes(), exact)
}
}
#[must_use]
pub fn looks_like_x87(bytes: [u8; 16]) -> bool {
let padding = &bytes[10..];
if padding.iter().any(|&byte| byte != 0) {
return false;
}
let extended = Extended80::from_padded_le_bytes(bytes);
let biased = extended.biased_exponent();
let integer_bit = extended.significand & X87_INTEGER != 0;
if biased == 0 {
return extended.significand == 0 || !integer_bit;
}
integer_bit
}