#[allow(unused_imports)]
use crate::prelude::*;
use core::cmp::Ordering;
use core::fmt;
use core::ops::{Add, Div, Mul, Neg, Sub};
use num_bigint::BigUint;
use num_integer::Integer;
use num_traits::{One, ToPrimitive, Zero};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Precision {
bits: u32,
}
impl Precision {
pub fn new(bits: u32) -> Self {
assert!(bits >= 1, "Precision must be at least 1 bit");
Self { bits }
}
pub fn bits(&self) -> u32 {
self.bits
}
pub const DOUBLE: Precision = Precision { bits: 53 };
pub const EXTENDED: Precision = Precision { bits: 64 };
pub const QUAD: Precision = Precision { bits: 113 };
pub const HIGH: Precision = Precision { bits: 256 };
}
impl Default for Precision {
fn default() -> Self {
Self::DOUBLE
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub enum RoundingMode {
#[default]
RoundNearest,
RoundTowardZero,
RoundUp,
RoundDown,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum SpecialValue {
None,
PosInfinity,
NegInfinity,
NaN,
}
#[derive(Clone)]
pub struct ArbitraryFloat {
sign: bool,
mantissa: BigUint,
exponent: i64,
precision: Precision,
special: SpecialValue,
}
fn shift_right_sticky(value: &BigUint, shift: usize) -> BigUint {
if shift == 0 {
return value.clone();
}
let value_bits = value.bits() as usize;
if shift >= value_bits {
return if value.is_zero() {
BigUint::zero()
} else {
BigUint::one()
};
}
let shifted = value >> shift;
let mask = (BigUint::one() << shift) - BigUint::one();
let dropped = value & &mask;
if dropped.is_zero() {
shifted
} else {
shifted | BigUint::one()
}
}
impl ArbitraryFloat {
fn new(sign: bool, mantissa: BigUint, exponent: i64, precision: Precision) -> Self {
Self {
sign,
mantissa,
exponent,
precision,
special: SpecialValue::None,
}
}
pub fn zero(precision: Precision) -> Self {
Self::new(false, BigUint::zero(), 0, precision)
}
pub fn one(precision: Precision) -> Self {
let mantissa = BigUint::one() << (precision.bits() - 1);
let exponent = 1 - precision.bits() as i64;
Self::new(false, mantissa, exponent, precision)
}
pub fn pos_infinity(precision: Precision) -> Self {
let mut f = Self::zero(precision);
f.special = SpecialValue::PosInfinity;
f
}
pub fn neg_infinity(precision: Precision) -> Self {
let mut f = Self::zero(precision);
f.special = SpecialValue::NegInfinity;
f
}
pub fn nan(precision: Precision) -> Self {
let mut f = Self::zero(precision);
f.special = SpecialValue::NaN;
f
}
pub fn is_zero(&self) -> bool {
self.special == SpecialValue::None && self.mantissa.is_zero()
}
pub fn is_pos_infinity(&self) -> bool {
self.special == SpecialValue::PosInfinity
}
pub fn is_neg_infinity(&self) -> bool {
self.special == SpecialValue::NegInfinity
}
pub fn is_infinity(&self) -> bool {
self.is_pos_infinity() || self.is_neg_infinity()
}
pub fn is_nan(&self) -> bool {
self.special == SpecialValue::NaN
}
pub fn is_finite(&self) -> bool {
self.special == SpecialValue::None
}
pub fn is_negative(&self) -> bool {
self.sign && !self.is_zero() && !self.is_nan()
}
pub fn is_positive(&self) -> bool {
!self.sign && !self.is_zero() && !self.is_nan()
}
pub fn precision(&self) -> Precision {
self.precision
}
pub fn from_f64(value: f64, precision: Precision) -> Self {
if value.is_nan() {
return Self::nan(precision);
}
if value.is_infinite() {
return if value > 0.0 {
Self::pos_infinity(precision)
} else {
Self::neg_infinity(precision)
};
}
if value == 0.0 {
let mut z = Self::zero(precision);
z.sign = value.is_sign_negative();
return z;
}
let bits = value.to_bits();
let sign = (bits >> 63) != 0;
let exp_bits = ((bits >> 52) & 0x7FF) as i64;
let mantissa_bits = bits & 0x000F_FFFF_FFFF_FFFF;
let exponent = if exp_bits == 0 {
1 - 1023 - 52
} else {
exp_bits - 1023 - 52
};
let mantissa = if exp_bits == 0 {
BigUint::from(mantissa_bits)
} else {
BigUint::from(mantissa_bits | (1u64 << 52))
};
let mut result = Self::new(sign, mantissa, exponent, precision);
result.normalize(RoundingMode::RoundNearest);
result
}
pub fn to_f64(&self, _rounding: RoundingMode) -> f64 {
if self.is_nan() {
return f64::NAN;
}
if self.is_pos_infinity() {
return f64::INFINITY;
}
if self.is_neg_infinity() {
return f64::NEG_INFINITY;
}
if self.is_zero() {
return if self.sign { -0.0 } else { 0.0 };
}
let precision_bits = self.precision.bits();
if precision_bits >= 53 {
let shift = (precision_bits - 53) as usize;
let f64_mantissa = (&self.mantissa >> shift).to_f64().unwrap_or(0.0);
let f64_exponent = self.exponent + shift as i64;
let result = f64_mantissa * 2.0f64.powi(f64_exponent as i32);
if self.sign { -result } else { result }
} else {
let mantissa_f64 = self.mantissa.to_f64().unwrap_or(0.0);
let result = mantissa_f64 * 2.0f64.powi(self.exponent as i32);
if self.sign { -result } else { result }
}
}
fn normalize(&mut self, rounding: RoundingMode) {
if self.mantissa.is_zero() || !self.is_finite() {
return;
}
let target_bits = self.precision.bits() as u64;
let current_bits = self.mantissa.bits();
if current_bits > target_bits {
let shift = current_bits - target_bits;
let (quotient, remainder) = self.mantissa.div_rem(&(BigUint::one() << shift as usize));
self.mantissa = quotient;
self.exponent += shift as i64;
let half = BigUint::one() << (shift as usize - 1);
let round_up = match rounding {
RoundingMode::RoundNearest => {
if remainder > half {
true
} else if remainder == half {
self.mantissa.bit(0)
} else {
false
}
}
RoundingMode::RoundUp => !self.sign && !remainder.is_zero(),
RoundingMode::RoundDown => self.sign && !remainder.is_zero(),
RoundingMode::RoundTowardZero => false,
};
if round_up {
self.mantissa += 1u32;
if self.mantissa.bits() > target_bits {
self.mantissa >>= 1;
self.exponent += 1;
}
}
} else if current_bits < target_bits {
let shift = target_bits - current_bits;
self.mantissa <<= shift as usize;
self.exponent -= shift as i64;
}
}
pub fn add(&self, other: &Self, rounding: RoundingMode) -> Self {
if self.is_nan() || other.is_nan() {
return Self::nan(self.precision.max(other.precision));
}
if self.is_infinity() || other.is_infinity() {
if self.is_pos_infinity() {
if other.is_neg_infinity() {
return Self::nan(self.precision);
}
return Self::pos_infinity(self.precision);
}
if self.is_neg_infinity() {
if other.is_pos_infinity() {
return Self::nan(self.precision);
}
return Self::neg_infinity(self.precision);
}
if other.is_pos_infinity() {
return Self::pos_infinity(other.precision);
}
return Self::neg_infinity(other.precision);
}
if self.is_zero() {
return other.clone();
}
if other.is_zero() {
return self.clone();
}
let result_precision = if self.precision.bits() >= other.precision.bits() {
self.precision
} else {
other.precision
};
let (m1, m2, exp, s1, s2) = self.align_with(other);
let (result_mantissa, result_sign) = if s1 == s2 {
(m1 + m2, s1)
} else {
match m1.cmp(&m2) {
Ordering::Greater => (m1 - m2, s1),
Ordering::Less => (m2 - m1, s2),
Ordering::Equal => return Self::zero(result_precision),
}
};
let mut result = Self::new(result_sign, result_mantissa, exp, result_precision);
result.normalize(rounding);
result
}
pub fn sub(&self, other: &Self, rounding: RoundingMode) -> Self {
let negated = other.neg();
self.add(&negated, rounding)
}
pub fn mul(&self, other: &Self, rounding: RoundingMode) -> Self {
let result_precision = if self.precision.bits() >= other.precision.bits() {
self.precision
} else {
other.precision
};
if self.is_nan() || other.is_nan() {
return Self::nan(result_precision);
}
if (self.is_zero() && other.is_infinity()) || (self.is_infinity() && other.is_zero()) {
return Self::nan(result_precision);
}
let result_sign = self.sign != other.sign;
if self.is_infinity() || other.is_infinity() {
return if result_sign {
Self::neg_infinity(result_precision)
} else {
Self::pos_infinity(result_precision)
};
}
if self.is_zero() || other.is_zero() {
let mut z = Self::zero(result_precision);
z.sign = result_sign;
return z;
}
let result_mantissa = &self.mantissa * &other.mantissa;
let result_exponent = self.exponent + other.exponent;
let mut result = Self::new(
result_sign,
result_mantissa,
result_exponent,
result_precision,
);
result.normalize(rounding);
result
}
pub fn div(&self, other: &Self, rounding: RoundingMode) -> Self {
let result_precision = if self.precision.bits() >= other.precision.bits() {
self.precision
} else {
other.precision
};
if self.is_nan() || other.is_nan() {
return Self::nan(result_precision);
}
let result_sign = self.sign != other.sign;
if (self.is_zero() && other.is_zero()) || (self.is_infinity() && other.is_infinity()) {
return Self::nan(result_precision);
}
if other.is_zero() {
return if result_sign {
Self::neg_infinity(result_precision)
} else {
Self::pos_infinity(result_precision)
};
}
if self.is_zero() {
let mut z = Self::zero(result_precision);
z.sign = result_sign;
return z;
}
if other.is_infinity() {
let mut z = Self::zero(result_precision);
z.sign = result_sign;
return z;
}
if self.is_infinity() {
return if result_sign {
Self::neg_infinity(result_precision)
} else {
Self::pos_infinity(result_precision)
};
}
let extra_bits = result_precision.bits() as usize + 10; let shifted_dividend = &self.mantissa << extra_bits;
let result_mantissa = &shifted_dividend / &other.mantissa;
let result_exponent = self.exponent - other.exponent - (extra_bits as i64);
let mut result = Self::new(
result_sign,
result_mantissa,
result_exponent,
result_precision,
);
result.normalize(rounding);
result
}
pub fn sqrt(&self, rounding: RoundingMode) -> Self {
if self.is_nan() || self.is_neg_infinity() || (self.is_negative() && !self.is_zero()) {
return Self::nan(self.precision);
}
if self.is_pos_infinity() {
return Self::pos_infinity(self.precision);
}
if self.is_zero() {
return Self::zero(self.precision);
}
let initial_guess = self.to_f64(RoundingMode::RoundNearest).sqrt();
let mut x = Self::from_f64(initial_guess, self.precision);
let two = Self::from_f64(2.0, self.precision);
let tolerance_bits = self.precision.bits() + 10;
for _ in 0..100 {
let s_div_x = Self::div(self, &x, rounding);
let sum = Self::add(&x, &s_div_x, rounding);
let x_new = Self::div(&sum, &two, rounding);
let diff = Self::sub(&x_new, &x, rounding);
if diff.is_zero()
|| (diff.mantissa.bits() as i64 + diff.exponent
< x_new.exponent - tolerance_bits as i64)
{
return x_new;
}
x = x_new;
}
x
}
pub fn neg(&self) -> Self {
if self.is_nan() {
return Self::nan(self.precision);
}
if self.is_pos_infinity() {
return Self::neg_infinity(self.precision);
}
if self.is_neg_infinity() {
return Self::pos_infinity(self.precision);
}
let mut result = self.clone();
result.sign = !result.sign;
result
}
pub fn abs(&self) -> Self {
if self.is_nan() {
return Self::nan(self.precision);
}
if self.is_neg_infinity() {
return Self::pos_infinity(self.precision);
}
let mut result = self.clone();
result.sign = false;
result
}
fn align_with(&self, other: &Self) -> (BigUint, BigUint, i64, bool, bool) {
let exp_diff = self.exponent - other.exponent;
if exp_diff >= 0 {
let shifted_other = shift_right_sticky(&other.mantissa, exp_diff as usize);
(
self.mantissa.clone(),
shifted_other,
self.exponent,
self.sign,
other.sign,
)
} else {
let shift = (-exp_diff) as usize;
let shifted_self = shift_right_sticky(&self.mantissa, shift);
(
shifted_self,
other.mantissa.clone(),
other.exponent,
self.sign,
other.sign,
)
}
}
pub fn partial_compare(&self, other: &Self) -> Option<Ordering> {
if self.is_nan() || other.is_nan() {
return None;
}
if self.is_pos_infinity() {
return if other.is_pos_infinity() {
Some(Ordering::Equal)
} else {
Some(Ordering::Greater)
};
}
if self.is_neg_infinity() {
return if other.is_neg_infinity() {
Some(Ordering::Equal)
} else {
Some(Ordering::Less)
};
}
if other.is_pos_infinity() {
return Some(Ordering::Less);
}
if other.is_neg_infinity() {
return Some(Ordering::Greater);
}
let self_zero = self.is_zero();
let other_zero = other.is_zero();
if self_zero && other_zero {
return Some(Ordering::Equal);
}
if self_zero {
return if other.sign {
Some(Ordering::Greater)
} else {
Some(Ordering::Less)
};
}
if other_zero {
return if self.sign {
Some(Ordering::Less)
} else {
Some(Ordering::Greater)
};
}
if self.sign != other.sign {
return if self.sign {
Some(Ordering::Less)
} else {
Some(Ordering::Greater)
};
}
let (m1, m2, _, _, _) = self.align_with(other);
let mag_cmp = m1.cmp(&m2);
if self.sign {
Some(mag_cmp.reverse())
} else {
Some(mag_cmp)
}
}
pub fn from_str(s: &str, precision: Precision) -> Option<Self> {
let s = s.trim();
if s.eq_ignore_ascii_case("nan") {
return Some(Self::nan(precision));
}
if s.eq_ignore_ascii_case("inf") || s.eq_ignore_ascii_case("+inf") {
return Some(Self::pos_infinity(precision));
}
if s.eq_ignore_ascii_case("-inf") {
return Some(Self::neg_infinity(precision));
}
if let Ok(f) = s.parse::<f64>() {
return Some(Self::from_f64(f, precision));
}
None
}
}
impl fmt::Debug for ArbitraryFloat {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.is_nan() {
write!(f, "NaN")
} else if self.is_pos_infinity() {
write!(f, "+Inf")
} else if self.is_neg_infinity() {
write!(f, "-Inf")
} else {
write!(
f,
"ArbitraryFloat {{ sign: {}, mantissa: {}, exp: {}, prec: {} }}",
self.sign,
self.mantissa,
self.exponent,
self.precision.bits()
)
}
}
}
impl fmt::Display for ArbitraryFloat {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.is_nan() {
write!(f, "NaN")
} else if self.is_pos_infinity() {
write!(f, "+Inf")
} else if self.is_neg_infinity() {
write!(f, "-Inf")
} else {
let value = self.to_f64(RoundingMode::RoundNearest);
if self.sign && !value.is_sign_negative() {
write!(f, "-{}", value.abs())
} else {
write!(f, "{}", value)
}
}
}
}
impl PartialEq for ArbitraryFloat {
fn eq(&self, other: &Self) -> bool {
self.partial_compare(other) == Some(Ordering::Equal)
}
}
impl PartialOrd for ArbitraryFloat {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
self.partial_compare(other)
}
}
impl Add for ArbitraryFloat {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
ArbitraryFloat::add(&self, &rhs, RoundingMode::RoundNearest)
}
}
impl Add for &ArbitraryFloat {
type Output = ArbitraryFloat;
fn add(self, rhs: Self) -> Self::Output {
ArbitraryFloat::add(self, rhs, RoundingMode::RoundNearest)
}
}
impl Sub for ArbitraryFloat {
type Output = Self;
fn sub(self, rhs: Self) -> Self::Output {
ArbitraryFloat::sub(&self, &rhs, RoundingMode::RoundNearest)
}
}
impl Sub for &ArbitraryFloat {
type Output = ArbitraryFloat;
fn sub(self, rhs: Self) -> Self::Output {
ArbitraryFloat::sub(self, rhs, RoundingMode::RoundNearest)
}
}
impl Mul for ArbitraryFloat {
type Output = Self;
fn mul(self, rhs: Self) -> Self::Output {
ArbitraryFloat::mul(&self, &rhs, RoundingMode::RoundNearest)
}
}
impl Mul for &ArbitraryFloat {
type Output = ArbitraryFloat;
fn mul(self, rhs: Self) -> Self::Output {
ArbitraryFloat::mul(self, rhs, RoundingMode::RoundNearest)
}
}
impl Div for ArbitraryFloat {
type Output = Self;
fn div(self, rhs: Self) -> Self::Output {
ArbitraryFloat::div(&self, &rhs, RoundingMode::RoundNearest)
}
}
impl Div for &ArbitraryFloat {
type Output = ArbitraryFloat;
fn div(self, rhs: Self) -> Self::Output {
ArbitraryFloat::div(self, rhs, RoundingMode::RoundNearest)
}
}
impl Neg for ArbitraryFloat {
type Output = Self;
fn neg(self) -> Self::Output {
ArbitraryFloat::neg(&self)
}
}
impl Neg for &ArbitraryFloat {
type Output = ArbitraryFloat;
fn neg(self) -> Self::Output {
ArbitraryFloat::neg(self)
}
}
impl Precision {
fn max(self, other: Self) -> Self {
if self.bits >= other.bits { self } else { other }
}
}
#[derive(Debug, Clone)]
pub struct ArbitraryFloatContext {
pub precision: Precision,
pub rounding: RoundingMode,
}
impl ArbitraryFloatContext {
pub fn new(precision: Precision, rounding: RoundingMode) -> Self {
Self {
precision,
rounding,
}
}
pub fn zero(&self) -> ArbitraryFloat {
ArbitraryFloat::zero(self.precision)
}
pub fn one(&self) -> ArbitraryFloat {
ArbitraryFloat::one(self.precision)
}
pub fn from_f64(&self, value: f64) -> ArbitraryFloat {
ArbitraryFloat::from_f64(value, self.precision)
}
pub fn add(&self, a: &ArbitraryFloat, b: &ArbitraryFloat) -> ArbitraryFloat {
a.add(b, self.rounding)
}
pub fn sub(&self, a: &ArbitraryFloat, b: &ArbitraryFloat) -> ArbitraryFloat {
a.sub(b, self.rounding)
}
pub fn mul(&self, a: &ArbitraryFloat, b: &ArbitraryFloat) -> ArbitraryFloat {
a.mul(b, self.rounding)
}
pub fn div(&self, a: &ArbitraryFloat, b: &ArbitraryFloat) -> ArbitraryFloat {
a.div(b, self.rounding)
}
pub fn sqrt(&self, a: &ArbitraryFloat) -> ArbitraryFloat {
a.sqrt(self.rounding)
}
}
impl Default for ArbitraryFloatContext {
fn default() -> Self {
Self::new(Precision::DOUBLE, RoundingMode::RoundNearest)
}
}
#[cfg(test)]
mod tests {
use super::*;
const EPSILON: f64 = 1e-10;
fn approx_eq(a: f64, b: f64) -> bool {
(a - b).abs() < EPSILON || (a.is_nan() && b.is_nan())
}
#[test]
fn test_precision_constants() {
assert_eq!(Precision::DOUBLE.bits(), 53);
assert_eq!(Precision::EXTENDED.bits(), 64);
assert_eq!(Precision::QUAD.bits(), 113);
assert_eq!(Precision::HIGH.bits(), 256);
}
#[test]
fn test_from_f64_basic() {
let prec = Precision::new(64);
let f = ArbitraryFloat::from_f64(core::f64::consts::PI, prec);
let back = f.to_f64(RoundingMode::RoundNearest);
assert!(approx_eq(back, core::f64::consts::PI));
}
#[test]
fn test_from_f64_special_values() {
let prec = Precision::new(64);
let nan = ArbitraryFloat::from_f64(f64::NAN, prec);
assert!(nan.is_nan());
let pos_inf = ArbitraryFloat::from_f64(f64::INFINITY, prec);
assert!(pos_inf.is_pos_infinity());
let neg_inf = ArbitraryFloat::from_f64(f64::NEG_INFINITY, prec);
assert!(neg_inf.is_neg_infinity());
let zero = ArbitraryFloat::from_f64(0.0, prec);
assert!(zero.is_zero());
}
#[test]
fn test_addition() {
let prec = Precision::new(64);
let a = ArbitraryFloat::from_f64(1.5, prec);
let b = ArbitraryFloat::from_f64(2.5, prec);
let sum = ArbitraryFloat::add(&a, &b, RoundingMode::RoundNearest);
assert!(approx_eq(sum.to_f64(RoundingMode::RoundNearest), 4.0));
}
#[test]
fn test_subtraction() {
let prec = Precision::new(64);
let a = ArbitraryFloat::from_f64(5.0, prec);
let b = ArbitraryFloat::from_f64(3.0, prec);
let diff = ArbitraryFloat::sub(&a, &b, RoundingMode::RoundNearest);
assert!(approx_eq(diff.to_f64(RoundingMode::RoundNearest), 2.0));
}
#[test]
fn test_multiplication() {
let prec = Precision::new(64);
let a = ArbitraryFloat::from_f64(3.0, prec);
let b = ArbitraryFloat::from_f64(4.0, prec);
let prod = ArbitraryFloat::mul(&a, &b, RoundingMode::RoundNearest);
assert!(approx_eq(prod.to_f64(RoundingMode::RoundNearest), 12.0));
}
#[test]
fn test_division() {
let prec = Precision::new(64);
let a = ArbitraryFloat::from_f64(10.0, prec);
let b = ArbitraryFloat::from_f64(4.0, prec);
let quot = ArbitraryFloat::div(&a, &b, RoundingMode::RoundNearest);
assert!(approx_eq(quot.to_f64(RoundingMode::RoundNearest), 2.5));
}
#[test]
fn test_sqrt() {
let prec = Precision::new(64);
let a = ArbitraryFloat::from_f64(4.0, prec);
let root = a.sqrt(RoundingMode::RoundNearest);
assert!(approx_eq(root.to_f64(RoundingMode::RoundNearest), 2.0));
}
#[test]
fn test_sqrt_2() {
let prec = Precision::new(128);
let a = ArbitraryFloat::from_f64(2.0, prec);
let root = a.sqrt(RoundingMode::RoundNearest);
let expected = 2.0f64.sqrt();
let result = root.to_f64(RoundingMode::RoundNearest);
assert!(
(result - expected).abs() < 1e-14,
"sqrt(2) = {}, expected {}",
result,
expected
);
}
#[test]
fn test_negation() {
let prec = Precision::new(64);
let a = ArbitraryFloat::from_f64(5.0, prec);
let neg_a = a.neg();
assert!(approx_eq(neg_a.to_f64(RoundingMode::RoundNearest), -5.0));
}
#[test]
fn test_abs() {
let prec = Precision::new(64);
let a = ArbitraryFloat::from_f64(-5.0, prec);
let abs_a = a.abs();
assert!(approx_eq(abs_a.to_f64(RoundingMode::RoundNearest), 5.0));
}
#[test]
fn test_comparison() {
let prec = Precision::new(64);
let a = ArbitraryFloat::from_f64(3.0, prec);
let b = ArbitraryFloat::from_f64(5.0, prec);
let c = ArbitraryFloat::from_f64(3.0, prec);
assert!(a < b);
assert!(b > a);
assert!(a == c);
assert!(a <= c);
assert!(a >= c);
}
#[test]
fn test_comparison_with_nan() {
let prec = Precision::new(64);
let a = ArbitraryFloat::from_f64(3.0, prec);
let nan = ArbitraryFloat::nan(prec);
assert!(a.partial_compare(&nan).is_none());
assert!(nan.partial_compare(&a).is_none());
assert!(nan.partial_compare(&nan).is_none());
}
#[test]
fn test_infinity_operations() {
let prec = Precision::new(64);
let pos_inf = ArbitraryFloat::pos_infinity(prec);
let neg_inf = ArbitraryFloat::neg_infinity(prec);
let one = ArbitraryFloat::from_f64(1.0, prec);
let sum = ArbitraryFloat::add(&pos_inf, &one, RoundingMode::RoundNearest);
assert!(sum.is_pos_infinity());
let sum2 = ArbitraryFloat::add(&pos_inf, &neg_inf, RoundingMode::RoundNearest);
assert!(sum2.is_nan());
let two = ArbitraryFloat::from_f64(2.0, prec);
let prod = ArbitraryFloat::mul(&pos_inf, &two, RoundingMode::RoundNearest);
assert!(prod.is_pos_infinity());
let zero = ArbitraryFloat::zero(prec);
let prod2 = ArbitraryFloat::mul(&pos_inf, &zero, RoundingMode::RoundNearest);
assert!(prod2.is_nan());
}
#[test]
fn test_zero_operations() {
let prec = Precision::new(64);
let zero = ArbitraryFloat::zero(prec);
let one = ArbitraryFloat::from_f64(1.0, prec);
let sum = ArbitraryFloat::add(&zero, &one, RoundingMode::RoundNearest);
assert!(approx_eq(sum.to_f64(RoundingMode::RoundNearest), 1.0));
let prod = ArbitraryFloat::mul(&zero, &one, RoundingMode::RoundNearest);
assert!(prod.is_zero());
let quot = ArbitraryFloat::div(&one, &zero, RoundingMode::RoundNearest);
assert!(quot.is_pos_infinity());
let quot2 = ArbitraryFloat::div(&zero, &zero, RoundingMode::RoundNearest);
assert!(quot2.is_nan());
}
#[test]
fn test_operator_overloads() {
let prec = Precision::new(64);
let a = ArbitraryFloat::from_f64(10.0, prec);
let b = ArbitraryFloat::from_f64(3.0, prec);
let sum = a.clone() + b.clone();
assert!(approx_eq(sum.to_f64(RoundingMode::RoundNearest), 13.0));
let diff = a.clone() - b.clone();
assert!(approx_eq(diff.to_f64(RoundingMode::RoundNearest), 7.0));
let prod = a.clone() * b.clone();
assert!(approx_eq(prod.to_f64(RoundingMode::RoundNearest), 30.0));
let quot = a.clone() / b.clone();
let result = quot.to_f64(RoundingMode::RoundNearest);
assert!(
(result - 10.0 / 3.0).abs() < 1e-10,
"10/3 = {}, expected {}",
result,
10.0 / 3.0
);
let neg = -a;
assert!(approx_eq(neg.to_f64(RoundingMode::RoundNearest), -10.0));
}
#[test]
fn test_context() {
let ctx = ArbitraryFloatContext::new(Precision::new(128), RoundingMode::RoundNearest);
let a = ctx.from_f64(3.5);
let b = ctx.from_f64(2.25);
let sum = ctx.add(&a, &b);
assert!(approx_eq(
sum.to_f64(RoundingMode::RoundNearest),
3.5 + 2.25
));
let sqrt_2 = ctx.sqrt(&ctx.from_f64(2.0));
assert!((sqrt_2.to_f64(RoundingMode::RoundNearest) - 2.0f64.sqrt()).abs() < 1e-14);
}
#[test]
fn test_from_str() {
let prec = Precision::new(64);
let nan = ArbitraryFloat::from_str("NaN", prec).expect("serialization failed");
assert!(nan.is_nan());
let inf = ArbitraryFloat::from_str("inf", prec).expect("serialization failed");
assert!(inf.is_pos_infinity());
let neg_inf = ArbitraryFloat::from_str("-inf", prec).expect("serialization failed");
assert!(neg_inf.is_neg_infinity());
let val = ArbitraryFloat::from_str("3.5", prec).expect("serialization failed");
assert!(approx_eq(val.to_f64(RoundingMode::RoundNearest), 3.5));
}
#[test]
fn test_high_precision() {
let low_prec = Precision::new(53);
let high_prec = Precision::new(256);
let one_low = ArbitraryFloat::from_f64(1.0, low_prec);
let three_low = ArbitraryFloat::from_f64(3.0, low_prec);
let div_low = ArbitraryFloat::div(&one_low, &three_low, RoundingMode::RoundNearest);
let result_low = ArbitraryFloat::mul(&div_low, &three_low, RoundingMode::RoundNearest);
let one_high = ArbitraryFloat::from_f64(1.0, high_prec);
let three_high = ArbitraryFloat::from_f64(3.0, high_prec);
let div_high = ArbitraryFloat::div(&one_high, &three_high, RoundingMode::RoundNearest);
let result_high = ArbitraryFloat::mul(&div_high, &three_high, RoundingMode::RoundNearest);
let error_low = (result_low.to_f64(RoundingMode::RoundNearest) - 1.0).abs();
let error_high = (result_high.to_f64(RoundingMode::RoundNearest) - 1.0).abs();
assert!(
error_high <= error_low + 1e-15,
"High precision error {} should be <= low precision error {}",
error_high,
error_low
);
}
#[test]
fn test_display() {
let prec = Precision::new(64);
let nan = ArbitraryFloat::nan(prec);
assert_eq!(format!("{}", nan), "NaN");
let pos_inf = ArbitraryFloat::pos_infinity(prec);
assert_eq!(format!("{}", pos_inf), "+Inf");
let neg_inf = ArbitraryFloat::neg_infinity(prec);
assert_eq!(format!("{}", neg_inf), "-Inf");
}
}