use crate::memory::bus::{MacMemoryBus, MemoryBus};
#[derive(Clone, Copy, Eq, PartialEq)]
pub(crate) struct Extended80 {
pub sign: bool,
pub exponent: u16, pub significand: u64, }
const BIAS: i32 = 16383;
const EXP_MAX: u16 = 0x7FFF;
#[allow(dead_code)]
impl Extended80 {
pub const ZERO: Self = Self {
sign: false,
exponent: 0,
significand: 0,
};
pub const NEG_ZERO: Self = Self {
sign: true,
exponent: 0,
significand: 0,
};
pub const INFINITY: Self = Self {
sign: false,
exponent: EXP_MAX,
significand: 0,
};
pub const NEG_INFINITY: Self = Self {
sign: true,
exponent: EXP_MAX,
significand: 0,
};
pub const NAN: Self = Self {
sign: false,
exponent: EXP_MAX,
significand: 0xC000_0000_0000_0000,
};
pub const ONE: Self = Self {
sign: false,
exponent: BIAS as u16,
significand: 1 << 63,
};
}
#[allow(dead_code)]
impl Extended80 {
pub fn is_zero(self) -> bool {
self.exponent == 0 && self.significand == 0
}
pub fn is_infinite(self) -> bool {
self.exponent == EXP_MAX && self.significand == 0
}
pub fn is_nan(self) -> bool {
self.exponent == EXP_MAX && self.significand != 0
}
pub fn is_normal(self) -> bool {
self.exponent != 0 && self.exponent != EXP_MAX
}
pub fn is_subnormal(self) -> bool {
self.exponent == 0 && self.significand != 0
}
pub fn is_sign_negative(self) -> bool {
self.sign
}
pub fn classify(self) -> i16 {
let sign = if self.sign { -1i16 } else { 1 };
if self.is_nan() {
if self.significand & (1 << 62) != 0 {
2 * sign } else {
sign }
} else if self.is_infinite() {
3 * sign
} else if self.is_zero() {
4 * sign
} else if self.is_normal() {
5 * sign
} else {
6 * sign }
}
}
#[allow(dead_code)]
impl Extended80 {
pub fn neg(self) -> Self {
Self {
sign: !self.sign,
..self
}
}
pub fn abs(self) -> Self {
Self {
sign: false,
..self
}
}
pub fn scalb(self, n: i16) -> Self {
if self.is_zero() || self.is_nan() || self.is_infinite() {
return self;
}
let new_exp = self.exponent as i32 + n as i32;
if new_exp >= EXP_MAX as i32 {
return if self.sign {
Self::NEG_INFINITY
} else {
Self::INFINITY
};
}
if new_exp <= 0 {
return if self.sign {
Self::NEG_ZERO
} else {
Self::ZERO
};
}
Self {
exponent: new_exp as u16,
..self
}
}
pub fn logb(self) -> Self {
if self.is_zero() {
return Self::NEG_INFINITY;
}
if self.is_nan() || self.is_infinite() {
return self.abs();
}
let exp = self.exponent as i32 - BIAS;
Self::from(exp as f64)
}
pub fn round_to_int(self) -> Self {
if self.is_zero() || self.is_nan() || self.is_infinite() {
return self;
}
let f = f64::from(self);
let rounded = rint(f);
Self::from(rounded)
}
pub fn trunc_to_int(self) -> Self {
if self.is_zero() || self.is_nan() || self.is_infinite() {
return self;
}
let f = f64::from(self);
Self::from(f.trunc())
}
}
#[allow(dead_code)]
fn rint(x: f64) -> f64 {
let rounded = x.round();
let diff = (x - rounded).abs();
if diff == 0.0 {
return rounded;
}
if (x.abs() - x.abs().floor() - 0.5).abs() < 1e-15 {
let floor = x.floor();
let ceil = x.ceil();
if floor as i64 % 2 == 0 {
floor
} else {
ceil
}
} else {
rounded
}
}
impl Extended80 {
pub fn read_from_bus(bus: &MacMemoryBus, addr: u32) -> Self {
let w0 = bus.read_word(addr);
let w1 = bus.read_word(addr + 2);
let w2 = bus.read_word(addr + 4);
let w3 = bus.read_word(addr + 6);
let w4 = bus.read_word(addr + 8);
Self {
sign: (w0 >> 15) != 0,
exponent: w0 & 0x7FFF,
significand: ((w1 as u64) << 48)
| ((w2 as u64) << 32)
| ((w3 as u64) << 16)
| (w4 as u64),
}
}
pub fn write_to_bus(self, bus: &mut MacMemoryBus, addr: u32) {
let sign_bit = if self.sign { 0x8000u16 } else { 0 };
bus.write_word(addr, sign_bit | self.exponent);
bus.write_word(addr + 2, (self.significand >> 48) as u16);
bus.write_word(addr + 4, (self.significand >> 32) as u16);
bus.write_word(addr + 6, (self.significand >> 16) as u16);
bus.write_word(addr + 8, self.significand as u16);
}
pub fn read_format(bus: &MacMemoryBus, addr: u32, fmt: u16) -> Self {
match fmt {
0 => Self::read_from_bus(bus, addr),
1 => Self::from(read_f64_be(bus, addr)),
2 => Self::from(read_f32_be(bus, addr)),
4 => Self::from(bus.read_word(addr) as i16),
5 => Self::from(bus.read_long(addr) as i32),
6 => Self::from_comp(read_i64_be(bus, addr)),
_ => Self::ZERO,
}
}
pub fn write_format(self, bus: &mut MacMemoryBus, addr: u32, fmt: u16) {
match fmt {
0 => self.write_to_bus(bus, addr),
1 => write_f64_be(bus, addr, f64::from(self)),
2 => write_f32_be(bus, addr, f64::from(self) as f32),
4 => bus.write_word(addr, (f64::from(self) as i16) as u16),
5 => bus.write_long(addr, f64::from(self) as i32 as u32),
6 => write_i64_be(bus, addr, self.to_comp()),
_ => {}
}
}
}
fn read_f64_be(bus: &MacMemoryBus, addr: u32) -> f64 {
let hi = bus.read_long(addr) as u64;
let lo = bus.read_long(addr + 4) as u64;
f64::from_bits((hi << 32) | lo)
}
fn write_f64_be(bus: &mut MacMemoryBus, addr: u32, val: f64) {
let bits = val.to_bits();
bus.write_long(addr, (bits >> 32) as u32);
bus.write_long(addr + 4, bits as u32);
}
fn read_f32_be(bus: &MacMemoryBus, addr: u32) -> f32 {
f32::from_bits(bus.read_long(addr))
}
fn write_f32_be(bus: &mut MacMemoryBus, addr: u32, val: f32) {
bus.write_long(addr, val.to_bits());
}
fn read_i64_be(bus: &MacMemoryBus, addr: u32) -> i64 {
let hi = bus.read_long(addr) as u64;
let lo = bus.read_long(addr + 4) as u64;
((hi << 32) | lo) as i64
}
fn write_i64_be(bus: &mut MacMemoryBus, addr: u32, val: i64) {
let bits = val as u64;
bus.write_long(addr, (bits >> 32) as u32);
bus.write_long(addr + 4, bits as u32);
}
impl From<f64> for Extended80 {
fn from(val: f64) -> Self {
if val.is_nan() {
return Self::NAN;
}
if val.is_infinite() {
return if val < 0.0 {
Self::NEG_INFINITY
} else {
Self::INFINITY
};
}
if val == 0.0 {
return if val.is_sign_negative() {
Self::NEG_ZERO
} else {
Self::ZERO
};
}
let sign = val < 0.0;
let bits = val.abs().to_bits();
let ieee_exp = ((bits >> 52) & 0x7FF) as i32;
let ieee_frac = bits & 0x000F_FFFF_FFFF_FFFF;
if ieee_exp == 0 {
return if sign { Self::NEG_ZERO } else { Self::ZERO };
}
let ext_exp = (ieee_exp - 1023 + BIAS) as u16;
let significand = (1u64 << 63) | (ieee_frac << 11);
Self {
sign,
exponent: ext_exp,
significand,
}
}
}
impl From<Extended80> for f64 {
fn from(ext: Extended80) -> f64 {
if ext.is_nan() {
return f64::NAN;
}
if ext.is_infinite() {
return if ext.sign {
f64::NEG_INFINITY
} else {
f64::INFINITY
};
}
if ext.is_zero() {
return if ext.sign { -0.0 } else { 0.0 };
}
let ext_exp = ext.exponent as i32;
let ieee_exp = ext_exp - BIAS + 1023;
if ieee_exp <= 0 {
return if ext.sign { -0.0 } else { 0.0 };
}
if ieee_exp >= 0x7FF {
return if ext.sign {
f64::NEG_INFINITY
} else {
f64::INFINITY
};
}
let frac = (ext.significand >> 11) & 0x000F_FFFF_FFFF_FFFF;
let bits = ((ext.sign as u64) << 63) | ((ieee_exp as u64) << 52) | frac;
f64::from_bits(bits)
}
}
impl From<f32> for Extended80 {
fn from(val: f32) -> Self {
Self::from(val as f64)
}
}
impl From<i16> for Extended80 {
fn from(val: i16) -> Self {
Self::from(val as f64)
}
}
impl From<i32> for Extended80 {
fn from(val: i32) -> Self {
Self::from(val as f64)
}
}
impl Extended80 {
pub fn from_comp(val: i64) -> Self {
Self::from(val as f64)
}
pub fn to_comp(self) -> i64 {
f64::from(self) as i64
}
}
impl PartialOrd for Extended80 {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
if self.is_nan() || other.is_nan() {
return None;
}
if self.is_zero() && other.is_zero() {
return Some(std::cmp::Ordering::Equal);
}
let a = f64::from(*self);
let b = f64::from(*other);
a.partial_cmp(&b)
}
}
#[allow(dead_code)]
impl Extended80 {
fn normalize(mut self) -> Self {
if self.significand == 0 {
self.exponent = 0;
return self;
}
let shift = self.significand.leading_zeros();
if shift > 0 {
self.significand <<= shift;
let new_exp = self.exponent as i32 - shift as i32;
if new_exp <= 0 {
return if self.sign {
Self::NEG_ZERO
} else {
Self::ZERO
};
}
self.exponent = new_exp as u16;
}
self
}
fn round_to_64(hi: u64, lo: u64) -> u64 {
if lo == 0 {
return hi;
}
let half = 1u64 << 63;
if lo > half || (lo == half && hi & 1 != 0) {
hi.wrapping_add(1)
} else {
hi
}
}
pub fn add(self, other: Self) -> Self {
if self.is_nan() {
return self;
}
if other.is_nan() {
return other;
}
if self.is_infinite() && other.is_infinite() {
return if self.sign == other.sign {
self
} else {
Self::NAN
};
}
if self.is_infinite() {
return self;
}
if other.is_infinite() {
return other;
}
if self.is_zero() && other.is_zero() {
return if self.sign && other.sign {
Self::NEG_ZERO
} else {
Self::ZERO
};
}
if self.is_zero() {
return other;
}
if other.is_zero() {
return self;
}
let (a, b) = if self.exponent > other.exponent
|| (self.exponent == other.exponent && self.significand >= other.significand)
{
(self, other)
} else {
(other, self)
};
let exp_diff = a.exponent as i32 - b.exponent as i32;
if exp_diff > 64 {
return a; }
let a_sig: u128 = a.significand as u128;
let b_sig: u128 = if exp_diff == 0 {
b.significand as u128
} else {
let shifted = (b.significand as u128) >> exp_diff as u32;
let mask = (1u128 << exp_diff as u32) - 1;
let lost = (b.significand as u128) & mask;
shifted | if lost != 0 { 1 } else { 0 }
};
let (sum, result_sign) = if a.sign == b.sign {
(a_sig + b_sig, a.sign)
} else {
if a_sig >= b_sig {
(a_sig - b_sig, a.sign)
} else {
(b_sig - a_sig, b.sign)
}
};
if sum == 0 {
return Self::ZERO;
}
let mut exp = a.exponent as i32;
let leading = 127 - sum.leading_zeros() as i32;
if leading > 63 {
let shift = (leading - 63) as u32;
exp += shift as i32;
let shifted = sum >> shift;
let round_bit = (sum >> (shift - 1)) & 1;
let sticky = if shift > 1 {
sum & ((1u128 << (shift - 1)) - 1)
} else {
0
};
let sig = shifted as u64;
let significand = if round_bit != 0 && (sticky != 0 || sig & 1 != 0) {
sig.wrapping_add(1)
} else {
sig
};
if exp >= EXP_MAX as i32 {
return if result_sign {
Self::NEG_INFINITY
} else {
Self::INFINITY
};
}
return Self {
sign: result_sign,
exponent: exp as u16,
significand,
};
} else if leading < 63 {
let shift = (63 - leading) as u32;
exp -= shift as i32;
if exp <= 0 {
return if result_sign {
Self::NEG_ZERO
} else {
Self::ZERO
};
}
let significand = (sum << shift) as u64;
return Self {
sign: result_sign,
exponent: exp as u16,
significand,
};
}
Self {
sign: result_sign,
exponent: exp as u16,
significand: sum as u64,
}
}
pub fn sub(self, other: Self) -> Self {
self.add(other.neg())
}
pub fn mul(self, other: Self) -> Self {
if self.is_nan() {
return self;
}
if other.is_nan() {
return other;
}
let result_sign = self.sign != other.sign;
if self.is_infinite() || other.is_infinite() {
if self.is_zero() || other.is_zero() {
return Self::NAN; }
return Self {
sign: result_sign,
..Self::INFINITY
};
}
if self.is_zero() || other.is_zero() {
return Self {
sign: result_sign,
..Self::ZERO
};
}
let product = self.significand as u128 * other.significand as u128;
let q = product >> 63; let round_bit = (product >> 62) & 1;
let sticky = product & ((1u128 << 62) - 1);
let mut new_exp = self.exponent as i32 + other.exponent as i32 - BIAS;
let (sig, rb, st) = if q > u64::MAX as u128 {
new_exp += 1;
let s = (q >> 1) as u64;
let r = q & 1;
let sticky2 = if round_bit != 0 || sticky != 0 {
1u128
} else {
0
};
(s, r, sticky2)
} else {
(q as u64, round_bit, sticky)
};
let significand = if rb != 0 && (st != 0 || sig & 1 != 0) {
sig.wrapping_add(1)
} else {
sig
};
if new_exp >= EXP_MAX as i32 {
return Self {
sign: result_sign,
..Self::INFINITY
};
}
if new_exp <= 0 {
return Self {
sign: result_sign,
..Self::ZERO
};
}
Self {
sign: result_sign,
exponent: new_exp as u16,
significand,
}
.normalize()
}
pub fn div(self, other: Self) -> Self {
if self.is_nan() {
return self;
}
if other.is_nan() {
return other;
}
let result_sign = self.sign != other.sign;
if self.is_infinite() && other.is_infinite() {
return Self::NAN;
}
if self.is_infinite() {
return Self {
sign: result_sign,
..Self::INFINITY
};
}
if other.is_infinite() {
return Self {
sign: result_sign,
..Self::ZERO
};
}
if other.is_zero() {
if self.is_zero() {
return Self::NAN;
}
return Self {
sign: result_sign,
..Self::INFINITY
};
}
if self.is_zero() {
return Self {
sign: result_sign,
..Self::ZERO
};
}
let dividend = (self.significand as u128) << 63;
let divisor = other.significand as u128;
let quotient = dividend / divisor;
let remainder = dividend % divisor;
let half_divisor = divisor >> 1;
let sig = quotient as u64;
let significand = if remainder > half_divisor || (remainder == half_divisor && sig & 1 != 0)
{
sig.wrapping_add(1)
} else {
sig
};
let new_exp = self.exponent as i32 - other.exponent as i32 + BIAS;
if new_exp >= EXP_MAX as i32 {
return Self {
sign: result_sign,
..Self::INFINITY
};
}
if new_exp <= 0 {
return Self {
sign: result_sign,
..Self::ZERO
};
}
Self {
sign: result_sign,
exponent: new_exp as u16,
significand,
}
.normalize()
}
pub fn rem(self, other: Self) -> Self {
if self.is_nan() || other.is_nan() || self.is_infinite() || other.is_zero() {
return Self::NAN;
}
if other.is_infinite() {
return self;
}
let a = f64::from(self);
let b = f64::from(other);
if b == 0.0 {
return Self::NAN;
}
let n = (a / b).round();
Self::from(a - n * b)
}
pub fn sqrt(self) -> Self {
if self.is_nan() {
return self;
}
if self.is_zero() {
return self;
} if self.sign {
return Self::NAN;
} if self.is_infinite() {
return self;
}
let approx = f64::from(self).sqrt();
let x = Self::from(approx);
let quotient = self.div(x);
let sum = x.add(quotient);
sum.scalb(-1)
}
}
#[allow(dead_code)]
impl Extended80 {
pub fn ln(self) -> Self {
Self::from(libm::log(f64::from(self)))
}
pub fn log2(self) -> Self {
Self::from(libm::log2(f64::from(self)))
}
pub fn ln1(self) -> Self {
Self::from(libm::log(1.0 + f64::from(self)))
}
pub fn log21(self) -> Self {
Self::from(libm::log2(1.0 + f64::from(self)))
}
pub fn exp(self) -> Self {
Self::from(libm::exp(f64::from(self)))
}
pub fn exp2(self) -> Self {
Self::from(libm::exp2(f64::from(self)))
}
pub fn exp1(self) -> Self {
Self::from(libm::pow(10.0, f64::from(self)) - 1.0)
}
pub fn sin(self) -> Self {
Self::from(libm::sin(f64::from(self)))
}
pub fn cos(self) -> Self {
Self::from(libm::cos(f64::from(self)))
}
pub fn tan(self) -> Self {
Self::from(libm::tan(f64::from(self)))
}
pub fn atan(self) -> Self {
Self::from(libm::atan(f64::from(self)))
}
pub fn powf(self, y: Self) -> Self {
Self::from(libm::pow(f64::from(self), f64::from(y)))
}
pub fn powi(self, n: i32) -> Self {
Self::from(libm::pow(f64::from(self), n as f64))
}
}
impl std::fmt::Debug for Extended80 {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if self.is_nan() {
write!(f, "NaN")
} else if self.is_infinite() {
write!(f, "{}Inf", if self.sign { "-" } else { "+" })
} else {
write!(f, "{:e}", f64::from(*self))
}
}
}
impl std::fmt::Display for Extended80 {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{:?}", self)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn zero_roundtrip() {
assert_eq!(Extended80::ZERO.exponent, 0);
assert_eq!(Extended80::ZERO.significand, 0);
#[allow(clippy::assertions_on_constants)]
{
assert!(!Extended80::ZERO.sign);
}
assert!(Extended80::ZERO.is_zero());
}
#[test]
fn neg_zero() {
assert!(Extended80::NEG_ZERO.is_zero());
assert!(Extended80::NEG_ZERO.is_sign_negative());
}
#[test]
fn infinity_classification() {
assert!(Extended80::INFINITY.is_infinite());
assert!(!Extended80::INFINITY.is_nan());
assert!(Extended80::NEG_INFINITY.is_infinite());
assert!(Extended80::NEG_INFINITY.is_sign_negative());
}
#[test]
fn nan_classification() {
assert!(Extended80::NAN.is_nan());
assert!(!Extended80::NAN.is_infinite());
}
#[test]
fn one_value() {
let v = f64::from(Extended80::ONE);
assert_eq!(v, 1.0);
}
#[test]
#[allow(clippy::approx_constant)] fn f64_roundtrip() {
for val in [1.0, -1.0, 0.5, 3.14159, 1e10, 1e-10, -42.5] {
let ext = Extended80::from(val);
let back = f64::from(ext);
assert!(
(back - val).abs() < 1e-10,
"roundtrip failed for {}: got {}",
val,
back
);
}
}
#[test]
fn add_basic() {
let a = Extended80::from(3.0);
let b = Extended80::from(2.0);
let result = f64::from(a.add(b));
assert_eq!(result, 5.0);
}
#[test]
fn sub_basic() {
let a = Extended80::from(5.0);
let b = Extended80::from(3.0);
let result = f64::from(a.sub(b));
assert_eq!(result, 2.0);
}
#[test]
fn mul_basic() {
let a = Extended80::from(3.0);
let b = Extended80::from(4.0);
let result = f64::from(a.mul(b));
assert_eq!(result, 12.0);
}
#[test]
fn div_basic() {
let a = Extended80::from(10.0);
let b = Extended80::from(4.0);
let result = f64::from(a.div(b));
assert_eq!(result, 2.5);
}
#[test]
fn div_by_zero() {
let a = Extended80::from(1.0);
let b = Extended80::ZERO;
assert!(a.div(b).is_infinite());
}
#[test]
fn sqrt_basic() {
let a = Extended80::from(4.0);
let result = f64::from(a.sqrt());
assert!((result - 2.0).abs() < 1e-10);
}
#[test]
fn sqrt_negative_is_nan() {
let a = Extended80::from(-1.0);
assert!(a.sqrt().is_nan());
}
#[test]
fn classify_values() {
assert_eq!(Extended80::NAN.classify(), 2); assert_eq!(Extended80::INFINITY.classify(), 3);
assert_eq!(Extended80::NEG_INFINITY.classify(), -3);
assert_eq!(Extended80::ZERO.classify(), 4);
assert_eq!(Extended80::NEG_ZERO.classify(), -4);
assert_eq!(Extended80::ONE.classify(), 5); }
#[test]
fn scalb_basic() {
let a = Extended80::from(1.0);
let result = f64::from(a.scalb(3)); assert_eq!(result, 8.0);
}
#[test]
fn neg_and_abs() {
let a = Extended80::from(5.0);
assert!(a.neg().is_sign_negative());
assert!(!a.neg().abs().is_sign_negative());
}
#[test]
fn comparison_ordering() {
let a = Extended80::from(1.0);
let b = Extended80::from(2.0);
assert!(a < b);
assert!(b > a);
assert_eq!(a.partial_cmp(&a), Some(std::cmp::Ordering::Equal));
}
#[test]
fn nan_comparison_is_none() {
let a = Extended80::from(1.0);
assert_eq!(a.partial_cmp(&Extended80::NAN), None);
}
#[test]
fn infinity_arithmetic() {
let inf = Extended80::INFINITY;
let one = Extended80::ONE;
assert!(inf.add(one).is_infinite());
assert!(inf.add(inf.neg()).is_nan()); assert!(inf.mul(Extended80::ZERO).is_nan()); }
}