use crate::error::{CoreError, CoreResult, ErrorContext};
const LIMB_BITS: usize = 64;
#[inline(always)]
fn comp_err(msg: impl Into<String>) -> CoreError {
CoreError::ComputationError(ErrorContext::new(msg))
}
#[derive(Debug, Clone)]
pub struct BigFloat {
pub sign: bool,
pub exponent: i64,
pub mantissa: Vec<u64>,
pub precision: usize,
}
impl BigFloat {
#[must_use]
pub fn zero(precision: usize) -> Self {
let prec = precision.max(1);
Self { sign: false, exponent: 0, mantissa: Vec::new(), precision: prec }
}
#[must_use]
pub fn one(precision: usize) -> Self {
let prec = precision.max(1);
let mut bf = Self { sign: false, exponent: 1, mantissa: Vec::new(), precision: prec };
let limbs = Self::limb_count(prec);
bf.mantissa = vec![0u64; limbs];
bf.mantissa[0] = 1u64 << 63; bf
}
#[must_use]
pub fn two(precision: usize) -> Self {
let prec = precision.max(1);
let limbs = Self::limb_count(prec);
let mut mantissa = vec![0u64; limbs];
mantissa[0] = 1u64 << 63;
Self { sign: false, exponent: 2, mantissa, precision: prec }
}
#[must_use]
pub fn from_f64(x: f64, precision: usize) -> Self {
let prec = precision.max(1);
if x == 0.0 || x.is_nan() || x.is_infinite() {
return Self::zero(prec);
}
let sign = x < 0.0;
let abs_x = x.abs();
let bits = abs_x.to_bits();
let biased_exp = (bits >> 52) as i32;
let frac = bits & ((1u64 << 52) - 1);
let sig: u64 = if biased_exp > 0 {
(1u64 << 52) | frac
} else {
frac };
let raw_exp = if biased_exp > 0 {
biased_exp as i64 - 1023 - 52 + 53
} else {
1i64 - 1023 - 52 + 53
};
let shift = 64 - 53; let top_limb = sig << shift;
let limbs = Self::limb_count(prec);
let mut mantissa = vec![0u64; limbs];
mantissa[0] = top_limb;
let mut bf = Self { sign, exponent: raw_exp, mantissa, precision: prec };
bf.normalise();
bf
}
#[must_use]
pub fn to_f64(&self) -> f64 {
if self.mantissa.is_empty() {
return 0.0;
}
let top = self.mantissa[0];
let sig = top >> 11; let exp_f64 = self.exponent - 53;
let value = sig as f64 * (exp_f64 as f64).exp2();
if self.sign { -value } else { value }
}
pub fn from_str(s: &str, precision: usize) -> CoreResult<Self> {
let s = s.trim();
let (neg, digits) = if let Some(rest) = s.strip_prefix('-') {
(true, rest)
} else {
(false, s)
};
let (int_part, frac_part) = if let Some(dot) = digits.find('.') {
(&digits[..dot], &digits[dot + 1..])
} else {
(digits, "")
};
let mut result = Self::zero(precision);
let ten = Self::from_f64(10.0, precision);
for ch in int_part.chars() {
let d = ch.to_digit(10).ok_or_else(|| comp_err(format!("invalid digit '{ch}'")))?;
result = result.mul(&ten, precision)?;
let digit = Self::from_f64(f64::from(d), precision);
result = result.add(&digit, precision)?;
}
let mut place = Self::from_f64(0.1, precision);
for ch in frac_part.chars() {
let d = ch.to_digit(10).ok_or_else(|| comp_err(format!("invalid digit '{ch}'")))?;
let digit = Self::from_f64(f64::from(d), precision);
let contribution = digit.mul(&place, precision)?;
result = result.add(&contribution, precision)?;
place = place.mul(&Self::from_f64(0.1, precision), precision)?;
}
result.sign = neg && !result.is_zero();
Ok(result)
}
#[must_use]
pub fn to_string_decimal(&self, sig_digits: usize) -> String {
if self.is_zero() {
return "0".to_string();
}
let v = self.to_f64();
let width = sig_digits.max(1);
format!("{:.prec$e}", v, prec = width - 1)
}
pub fn add(&self, other: &BigFloat, precision: usize) -> CoreResult<BigFloat> {
if self.is_zero() {
return Ok(other.with_precision(precision));
}
if other.is_zero() {
return Ok(self.with_precision(precision));
}
if self.sign == other.sign {
let mut result = self.add_magnitudes(other, precision)?;
result.sign = self.sign;
Ok(result)
} else {
let cmp = self.cmp_magnitude(other);
match cmp {
std::cmp::Ordering::Equal => Ok(BigFloat::zero(precision)),
std::cmp::Ordering::Greater => {
let mut result = self.sub_magnitudes(other, precision)?;
result.sign = self.sign;
Ok(result)
}
std::cmp::Ordering::Less => {
let mut result = other.sub_magnitudes(self, precision)?;
result.sign = other.sign;
Ok(result)
}
}
}
}
pub fn sub(&self, other: &BigFloat, precision: usize) -> CoreResult<BigFloat> {
let neg_other = BigFloat {
sign: !other.sign && !other.is_zero(),
exponent: other.exponent,
mantissa: other.mantissa.clone(),
precision: other.precision,
};
self.add(&neg_other, precision)
}
pub fn mul(&self, other: &BigFloat, precision: usize) -> CoreResult<BigFloat> {
if self.is_zero() || other.is_zero() {
return Ok(BigFloat::zero(precision));
}
let sign = self.sign ^ other.sign;
let result_mantissa = multiply_mantissas(&self.mantissa, &other.mantissa);
let exponent = self.exponent + other.exponent;
let mut bf = BigFloat {
sign,
exponent,
mantissa: result_mantissa,
precision,
};
bf.normalise();
bf.truncate(precision);
Ok(bf)
}
pub fn div(&self, other: &BigFloat, precision: usize) -> CoreResult<BigFloat> {
if other.is_zero() {
return Err(comp_err("division by zero in BigFloat::div"));
}
if self.is_zero() {
return Ok(BigFloat::zero(precision));
}
let sign = self.sign ^ other.sign;
let work_prec = precision + 64;
let recip_approx = 1.0 / other.to_f64();
let mut x = BigFloat::from_f64(recip_approx, work_prec);
let two = BigFloat::from_f64(2.0, work_prec);
for _ in 0..100 {
let ox = other.mul(&x, work_prec)?;
let correction = two.sub(&ox, work_prec)?;
let x_new = x.mul(&correction, work_prec)?;
let diff = x_new.sub(&x, work_prec)?;
if diff.is_zero() || diff.exponent < x.exponent - (work_prec as i64) {
x = x_new;
break;
}
x = x_new;
}
let mut result = self.mul(&x, work_prec)?;
result.sign = sign;
result.truncate(precision);
Ok(result)
}
pub fn sqrt(&self, precision: usize) -> CoreResult<BigFloat> {
if self.sign {
return Err(comp_err("sqrt of negative number"));
}
if self.is_zero() {
return Ok(BigFloat::zero(precision));
}
let work_prec = precision + 64;
let approx = self.to_f64().sqrt();
let mut x = BigFloat::from_f64(approx, work_prec);
let half = BigFloat::from_f64(0.5, work_prec);
let two = BigFloat::from_f64(2.0, work_prec);
for _ in 0..100 {
let xnew = x.add(&self.div(&x, work_prec)?, work_prec)?.mul(&half, work_prec)?;
let diff = xnew.sub(&x, work_prec)?;
if diff.is_zero() {
x = xnew;
break;
}
let diff_exp = diff.exponent;
let target_exp = x.exponent - (work_prec as i64);
if diff_exp < target_exp {
x = xnew;
break;
}
x = xnew;
let _ = two.clone(); }
x.truncate(precision);
Ok(x)
}
pub fn ln(&self, precision: usize) -> CoreResult<BigFloat> {
if self.sign || self.is_zero() {
return Err(comp_err("ln of non-positive number"));
}
let work_prec = precision + 64;
if precision <= 52 {
let v = self.to_f64().ln();
return Ok(BigFloat::from_f64(v, precision));
}
let k = self.exponent; let mut y = self.with_precision(work_prec);
y.exponent = 0; let s = ((work_prec as f64 / 3.0).ceil() as i64).max(4);
let mut z = y.clone();
z.exponent += s;
let four = BigFloat::from_f64(4.0, work_prec);
let four_over_z = four.div(&z, work_prec)?;
let one = BigFloat::one(work_prec);
let agm_val = agm(&one, &four_over_z, work_prec)?;
let pi_val = pi_bf(work_prec)?;
let two = BigFloat::from_f64(2.0, work_prec);
let ln_y = pi_val.div(&(two.mul(&agm_val, work_prec)?), work_prec)?
.sub(&BigFloat::from_f64(s as f64, work_prec)
.mul(&ln2_bf(work_prec)?, work_prec)?, work_prec)?;
let k_ln2 = BigFloat::from_f64(k as f64, work_prec).mul(&ln2_bf(work_prec)?, work_prec)?;
let mut result = ln_y.add(&k_ln2, work_prec)?;
result.truncate(precision);
Ok(result)
}
pub fn exp(&self, precision: usize) -> CoreResult<BigFloat> {
if self.is_zero() {
return Ok(BigFloat::one(precision));
}
let work_prec = precision + 64;
let ln2 = ln2_bf(work_prec)?;
let x_over_ln2 = self.div(&ln2, work_prec)?;
let k = x_over_ln2.to_f64().round() as i64;
let k_ln2 = BigFloat::from_f64(k as f64, work_prec).mul(&ln2, work_prec)?;
let r = self.sub(&k_ln2, work_prec)?;
let n_terms = (work_prec as f64 / (work_prec as f64).log2() + 10.0).ceil() as usize;
let n_terms = n_terms.max(20).min(1000);
let one = BigFloat::one(work_prec);
let mut sum = one.clone();
let mut term = one.clone();
for n in 1..=n_terms {
term = term.mul(&r, work_prec)?
.div(&BigFloat::from_f64(n as f64, work_prec), work_prec)?;
let new_sum = sum.add(&term, work_prec)?;
if !term.is_zero() && term.exponent < sum.exponent - (work_prec as i64) {
sum = new_sum;
break;
}
sum = new_sum;
}
sum.exponent += k;
sum.truncate(precision);
Ok(sum)
}
pub fn pi(precision: usize) -> CoreResult<BigFloat> {
pi_bf(precision)
}
pub fn e(precision: usize) -> CoreResult<BigFloat> {
BigFloat::one(precision).exp(precision)
}
pub fn ln2(precision: usize) -> CoreResult<BigFloat> {
ln2_bf(precision)
}
pub fn sqrt2(precision: usize) -> CoreResult<BigFloat> {
BigFloat::from_f64(2.0, precision).sqrt(precision)
}
#[must_use]
pub fn is_zero(&self) -> bool {
self.mantissa.is_empty() || self.mantissa.iter().all(|&l| l == 0)
}
#[must_use]
pub fn is_negative(&self) -> bool {
self.sign && !self.is_zero()
}
#[inline]
fn limb_count(prec: usize) -> usize {
(prec + LIMB_BITS - 1) / LIMB_BITS
}
fn with_precision(&self, precision: usize) -> BigFloat {
let mut bf = self.clone();
bf.precision = precision;
bf.truncate(precision);
bf
}
fn normalise(&mut self) {
if self.is_zero() {
self.mantissa.clear();
return;
}
while !self.mantissa.is_empty() && self.mantissa[0] == 0 {
self.mantissa.remove(0);
self.exponent -= LIMB_BITS as i64;
}
if self.mantissa.is_empty() {
return;
}
let leading = self.mantissa[0].leading_zeros() as usize;
if leading > 0 {
left_shift_mantissa(&mut self.mantissa, leading);
self.exponent -= leading as i64;
}
}
fn truncate(&mut self, precision: usize) {
if self.is_zero() {
return;
}
let limbs = Self::limb_count(precision);
if self.mantissa.len() <= limbs {
self.mantissa.resize(limbs, 0);
return;
}
let round_bit = (self.mantissa[limbs] >> 63) != 0;
self.mantissa.truncate(limbs);
if round_bit {
let mut carry = 1u64;
for limb in self.mantissa.iter_mut().rev() {
let (new_limb, c) = limb.overflowing_add(carry);
*limb = new_limb;
carry = if c { 1 } else { 0 };
if carry == 0 {
break;
}
}
if carry != 0 {
right_shift_mantissa(&mut self.mantissa, 1);
self.mantissa[0] |= 1u64 << 63;
self.exponent += 1;
}
}
}
fn cmp_magnitude(&self, other: &BigFloat) -> std::cmp::Ordering {
if self.exponent != other.exponent {
return self.exponent.cmp(&other.exponent);
}
let len = self.mantissa.len().min(other.mantissa.len());
for i in 0..len {
if self.mantissa[i] != other.mantissa[i] {
return self.mantissa[i].cmp(&other.mantissa[i]);
}
}
self.mantissa.len().cmp(&other.mantissa.len())
}
fn add_magnitudes(&self, other: &BigFloat, precision: usize) -> CoreResult<BigFloat> {
let (a, b, exp) = align_exponents(self, other, precision);
let max_len = a.len().max(b.len());
let mut a_ext = vec![0u64; max_len + 1];
let mut b_ext = vec![0u64; max_len + 1];
for (i, &v) in a.iter().enumerate() {
if i + 1 < a_ext.len() {
a_ext[i + 1] = v;
}
}
for (i, &v) in b.iter().enumerate() {
if i + 1 < b_ext.len() {
b_ext[i + 1] = v;
}
}
let total_len = max_len + 1;
let mut result = vec![0u64; total_len];
let mut carry = 0u64;
for i in (0..total_len).rev() {
let (s1, c1) = a_ext[i].overflowing_add(b_ext[i]);
let (s2, c2) = s1.overflowing_add(carry);
result[i] = s2;
carry = if c1 || c2 { 1 } else { 0 };
}
let mut bf = BigFloat { sign: false, exponent: exp + LIMB_BITS as i64, mantissa: result, precision };
bf.normalise();
bf.truncate(precision);
Ok(bf)
}
fn sub_magnitudes(&self, other: &BigFloat, precision: usize) -> CoreResult<BigFloat> {
let (a, b, exp) = align_exponents(self, other, precision);
let max_len = a.len().max(b.len());
let a_ext = extend_limbs(&a, max_len);
let b_ext = extend_limbs(&b, max_len);
let mut result = vec![0u64; max_len];
let mut borrow = 0u64;
for i in (0..max_len).rev() {
let (d1, b1) = a_ext[i].overflowing_sub(b_ext[i]);
let (d2, b2) = d1.overflowing_sub(borrow);
result[i] = d2;
borrow = if b1 || b2 { 1 } else { 0 };
}
let mut bf = BigFloat { sign: false, exponent: exp, mantissa: result, precision };
bf.normalise();
bf.truncate(precision);
Ok(bf)
}
}
fn align_exponents(a: &BigFloat, b: &BigFloat, precision: usize) -> (Vec<u64>, Vec<u64>, i64) {
let limbs = BigFloat::limb_count(precision) + 2;
let a_exp = a.exponent;
let b_exp = b.exponent;
let (a_m, b_m, exp) = if a_exp >= b_exp {
let shift = (a_exp - b_exp) as usize;
let mut b_shifted = extend_limbs(&b.mantissa, limbs + (shift / LIMB_BITS) + 2);
right_shift_mantissa(&mut b_shifted, shift);
(extend_limbs(&a.mantissa, limbs), b_shifted, a_exp)
} else {
let shift = (b_exp - a_exp) as usize;
let mut a_shifted = extend_limbs(&a.mantissa, limbs + (shift / LIMB_BITS) + 2);
right_shift_mantissa(&mut a_shifted, shift);
(a_shifted, extend_limbs(&b.mantissa, limbs), b_exp)
};
(a_m, b_m, exp)
}
fn extend_limbs(limbs: &[u64], target: usize) -> Vec<u64> {
let mut v = limbs.to_vec();
if v.len() < target {
v.resize(target, 0);
}
v
}
fn left_shift_mantissa(mantissa: &mut Vec<u64>, shift: usize) {
if shift == 0 || mantissa.is_empty() {
return;
}
let limb_shift = shift / LIMB_BITS;
let bit_shift = shift % LIMB_BITS;
for _ in 0..limb_shift {
if !mantissa.is_empty() {
mantissa.remove(0);
}
}
if bit_shift > 0 && !mantissa.is_empty() {
let mut carry = 0u64;
for limb in mantissa.iter_mut().rev() {
let new_carry = if bit_shift < 64 { *limb >> (64 - bit_shift) } else { 0 };
*limb = limb.wrapping_shl(bit_shift as u32) | carry;
carry = new_carry;
}
}
}
fn right_shift_mantissa(mantissa: &mut Vec<u64>, shift: usize) {
if shift == 0 || mantissa.is_empty() {
return;
}
let limb_shift = shift / LIMB_BITS;
let bit_shift = shift % LIMB_BITS;
for _ in 0..limb_shift {
mantissa.insert(0, 0u64);
}
if bit_shift > 0 {
let mut carry = 0u64;
for limb in mantissa.iter_mut() {
let new_carry = if bit_shift < 64 { *limb << (64 - bit_shift) } else { 0 };
*limb = limb.wrapping_shr(bit_shift as u32) | carry;
carry = new_carry;
}
}
}
fn multiply_mantissas(a: &[u64], b: &[u64]) -> Vec<u64> {
if a.is_empty() || b.is_empty() {
return Vec::new();
}
let len = a.len() + b.len();
let mut out = vec![0u64; len];
for i in (0..a.len()).rev() {
let mut carry = 0u128;
for j in (0..b.len()).rev() {
let pos = i + j + 1;
let prod = a[i] as u128 * b[j] as u128 + out[pos] as u128 + carry;
out[pos] = prod as u64;
carry = prod >> 64;
}
let mut pos = i;
while carry > 0 {
let val = out[pos] as u128 + carry;
out[pos] = val as u64;
carry = val >> 64;
if pos == 0 {
break;
}
pos -= 1;
}
}
out
}
fn divide_mantissas(num: &[u64], den: &[u64], precision: usize) -> Vec<u64> {
let limbs = BigFloat::limb_count(precision) + 2;
let total_bits = limbs * LIMB_BITS;
let work_len = (den.len().max(num.len()) + limbs + 2).max(4);
let mut rem = vec![0u64; work_len];
for (i, &v) in num.iter().enumerate() {
if i < work_len {
rem[i] = v;
}
}
let mut den_w = vec![0u64; work_len];
for (i, &v) in den.iter().enumerate() {
if i < work_len {
den_w[i] = v;
}
}
let mut quotient = vec![0u64; limbs];
for bit_idx in 0..total_bits {
let ge = bigint_ge(&rem, &den_w);
if ge {
let limb_idx = bit_idx / LIMB_BITS;
let bit_pos = 63 - (bit_idx % LIMB_BITS);
quotient[limb_idx] |= 1u64 << bit_pos;
bigint_sub_inplace(&mut rem, &den_w);
}
bigint_shl1(&mut rem);
}
quotient
}
fn bigint_ge(a: &[u64], b: &[u64]) -> bool {
let len = a.len().max(b.len());
for i in 0..len {
let ai = if i < a.len() { a[i] } else { 0 };
let bi = if i < b.len() { b[i] } else { 0 };
if ai > bi {
return true;
}
if ai < bi {
return false;
}
}
true }
fn bigint_sub_inplace(a: &mut [u64], b: &[u64]) {
let mut borrow = 0u64;
let len = a.len();
for i in (0..len).rev() {
let bi = if i < b.len() { b[i] } else { 0 };
let (d1, b1) = a[i].overflowing_sub(bi);
let (d2, b2) = d1.overflowing_sub(borrow);
a[i] = d2;
borrow = if b1 || b2 { 1 } else { 0 };
}
}
fn bigint_shl1(a: &mut [u64]) {
let mut carry = 0u64;
for i in (0..a.len()).rev() {
let new_carry = a[i] >> 63;
a[i] = (a[i] << 1) | carry;
carry = new_carry;
}
}
fn agm(a: &BigFloat, b: &BigFloat, precision: usize) -> CoreResult<BigFloat> {
let work_prec = precision + 32;
let mut a_k = a.with_precision(work_prec);
let mut b_k = b.with_precision(work_prec);
let half = BigFloat::from_f64(0.5, work_prec);
for _ in 0..200 {
let a_next = a_k.add(&b_k, work_prec)?.mul(&half, work_prec)?;
let b_next = a_k.mul(&b_k, work_prec)?.sqrt(work_prec)?;
let diff = a_next.sub(&b_next, work_prec)?;
let old = a_k.clone();
a_k = a_next;
b_k = b_next;
if diff.is_zero() || (!diff.is_zero() && diff.exponent < old.exponent - (work_prec as i64)) {
break;
}
}
Ok(a_k.with_precision(precision))
}
fn pi_bf(precision: usize) -> CoreResult<BigFloat> {
let work_prec = precision + 64;
let one = BigFloat::one(work_prec);
let two = BigFloat::from_f64(2.0, work_prec);
let half = BigFloat::from_f64(0.5, work_prec);
let inv_sqrt2 = two.sqrt(work_prec)?.div(&two, work_prec)?; let mut a = one.clone();
let mut b = inv_sqrt2;
let mut t = BigFloat::from_f64(0.25, work_prec);
let mut p = one.clone();
for _ in 0..200 {
let a_next = a.add(&b, work_prec)?.mul(&half, work_prec)?;
let b_next = a.mul(&b, work_prec)?.sqrt(work_prec)?;
let diff = a.sub(&a_next, work_prec)?; let diff_sq = diff.mul(&diff, work_prec)?.mul(&p, work_prec)?;
let t_next = t.sub(&diff_sq, work_prec)?;
let p_next = p.mul(&two, work_prec)?;
let old_a = a.clone();
a = a_next;
b = b_next;
let conv_check = a.sub(&old_a, work_prec)?;
t = t_next;
p = p_next;
if conv_check.is_zero() || conv_check.exponent < a.exponent - (work_prec as i64) {
break;
}
}
let ab = a.add(&b, work_prec)?;
let numerator = ab.mul(&ab, work_prec)?;
let four_t = BigFloat::from_f64(4.0, work_prec).mul(&t, work_prec)?;
let mut result = numerator.div(&four_t, work_prec)?;
result.truncate(precision);
Ok(result)
}
fn ln2_bf(precision: usize) -> CoreResult<BigFloat> {
let work_prec = precision + 64;
if precision <= 52 {
return Ok(BigFloat::from_f64(std::f64::consts::LN_2, precision));
}
let one = BigFloat::one(work_prec);
let three = BigFloat::from_f64(3.0, work_prec);
let z = one.div(&three, work_prec)?; let z2 = z.mul(&z, work_prec)?; let mut sum = z.clone(); let mut term = z.clone();
let n_terms = (work_prec as f64 / std::f64::consts::LOG2_10 / 2.0 + 10.0).ceil() as usize;
for k in 1..=n_terms {
term = term.mul(&z2, work_prec)?;
let denom = BigFloat::from_f64((2 * k + 1) as f64, work_prec);
let t = term.div(&denom, work_prec)?;
let new_sum = sum.add(&t, work_prec)?;
if !t.is_zero() && t.exponent < sum.exponent - (work_prec as i64) {
sum = new_sum;
break;
}
sum = new_sum;
}
let two = BigFloat::from_f64(2.0, work_prec);
let mut result = two.mul(&sum, work_prec)?;
result.truncate(precision);
Ok(result)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_from_f64_roundtrip() {
for &v in &[1.0f64, -1.0, 3.14, 0.5, 1024.0, -0.125] {
let bf = BigFloat::from_f64(v, 128);
let back = bf.to_f64();
let rel = (back - v).abs() / v.abs().max(1e-300);
assert!(rel < 1e-14, "roundtrip failed for {v}: got {back}");
}
}
#[test]
fn test_add() {
let a = BigFloat::from_f64(1.5, 128);
let b = BigFloat::from_f64(2.5, 128);
let c = a.add(&b, 128).expect("should succeed");
let diff = (c.to_f64() - 4.0).abs();
assert!(diff < 1e-14, "add: expected 4.0, got {}", c.to_f64());
}
#[test]
fn test_sub() {
let a = BigFloat::from_f64(5.0, 128);
let b = BigFloat::from_f64(3.0, 128);
let c = a.sub(&b, 128).expect("should succeed");
let diff = (c.to_f64() - 2.0).abs();
assert!(diff < 1e-14, "sub: expected 2.0, got {}", c.to_f64());
}
#[test]
fn test_mul() {
let a = BigFloat::from_f64(3.0, 128);
let b = BigFloat::from_f64(4.0, 128);
let c = a.mul(&b, 128).expect("should succeed");
let diff = (c.to_f64() - 12.0).abs();
assert!(diff < 1e-13, "mul: expected 12.0, got {}", c.to_f64());
}
#[test]
fn test_div() {
let a = BigFloat::from_f64(10.0, 128);
let b = BigFloat::from_f64(4.0, 128);
let c = a.div(&b, 128).expect("should succeed");
let diff = (c.to_f64() - 2.5).abs();
assert!(diff < 1e-13, "div: expected 2.5, got {}", c.to_f64());
}
#[test]
fn test_div_by_zero() {
let a = BigFloat::from_f64(1.0, 64);
let z = BigFloat::zero(64);
assert!(a.div(&z, 64).is_err());
}
#[test]
fn test_sqrt() {
let a = BigFloat::from_f64(2.0, 128);
let s = a.sqrt(128).expect("should succeed");
let expected = 2.0f64.sqrt();
let diff = (s.to_f64() - expected).abs();
assert!(diff < 1e-14, "sqrt(2): expected {expected}, got {}", s.to_f64());
}
#[test]
fn test_sqrt_negative() {
let a = BigFloat::from_f64(-1.0, 64);
assert!(a.sqrt(64).is_err());
}
#[test]
fn test_pi() {
let pi = BigFloat::pi(128).expect("should succeed");
let diff = (pi.to_f64() - std::f64::consts::PI).abs();
assert!(diff < 1e-12, "pi: expected {}, got {}", std::f64::consts::PI, pi.to_f64());
}
#[test]
fn test_ln2() {
let ln2 = BigFloat::ln2(128).expect("should succeed");
let expected = std::f64::consts::LN_2;
let diff = (ln2.to_f64() - expected).abs();
assert!(diff < 1e-13, "ln2: expected {expected}, got {}", ln2.to_f64());
}
#[test]
fn test_sqrt2() {
let s = BigFloat::sqrt2(128).expect("should succeed");
let expected = std::f64::consts::SQRT_2;
let diff = (s.to_f64() - expected).abs();
assert!(diff < 1e-14, "sqrt2: expected {expected}, got {}", s.to_f64());
}
#[test]
fn test_exp() {
let one = BigFloat::one(128);
let e = one.exp(128).expect("should succeed");
let expected = std::f64::consts::E;
let diff = (e.to_f64() - expected).abs();
assert!(diff < 1e-13, "e: expected {expected}, got {}", e.to_f64());
}
#[test]
fn test_zero_identity() {
let a = BigFloat::from_f64(5.0, 128);
let z = BigFloat::zero(128);
let sum = a.add(&z, 128).expect("should succeed");
let diff = (sum.to_f64() - 5.0).abs();
assert!(diff < 1e-14);
}
#[test]
fn test_negative_add() {
let a = BigFloat::from_f64(3.0, 128);
let b = BigFloat::from_f64(-1.5, 128);
let c = a.add(&b, 128).expect("should succeed");
let diff = (c.to_f64() - 1.5).abs();
assert!(diff < 1e-14);
}
}