1#[allow(unused_imports)]
41use crate::prelude::*;
42use core::cmp::Ordering;
43use core::fmt;
44use core::ops::{Add, Div, Mul, Neg, Sub};
45use num_bigint::BigUint;
46use num_integer::Integer;
47use num_traits::{One, ToPrimitive, Zero};
48
49#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
54pub struct Precision {
55 bits: u32,
57}
58
59impl Precision {
60 pub fn new(bits: u32) -> Self {
71 assert!(bits >= 1, "Precision must be at least 1 bit");
72 Self { bits }
73 }
74
75 pub fn bits(&self) -> u32 {
77 self.bits
78 }
79
80 pub const DOUBLE: Precision = Precision { bits: 53 };
82
83 pub const EXTENDED: Precision = Precision { bits: 64 };
85
86 pub const QUAD: Precision = Precision { bits: 113 };
88
89 pub const HIGH: Precision = Precision { bits: 256 };
91}
92
93impl Default for Precision {
94 fn default() -> Self {
95 Self::DOUBLE
96 }
97}
98
99#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
103pub enum RoundingMode {
104 #[default]
106 RoundNearest,
107 RoundTowardZero,
109 RoundUp,
111 RoundDown,
113}
114
115#[derive(Debug, Clone, Copy, PartialEq, Eq)]
117enum SpecialValue {
118 None,
120 PosInfinity,
122 NegInfinity,
124 NaN,
126}
127
128#[derive(Clone)]
135pub struct ArbitraryFloat {
136 sign: bool,
138 mantissa: BigUint,
140 exponent: i64,
142 precision: Precision,
144 special: SpecialValue,
146}
147
148impl ArbitraryFloat {
149 fn new(sign: bool, mantissa: BigUint, exponent: i64, precision: Precision) -> Self {
157 Self {
158 sign,
159 mantissa,
160 exponent,
161 precision,
162 special: SpecialValue::None,
163 }
164 }
165
166 pub fn zero(precision: Precision) -> Self {
168 Self::new(false, BigUint::zero(), 0, precision)
169 }
170
171 pub fn one(precision: Precision) -> Self {
173 let mantissa = BigUint::one() << (precision.bits() - 1);
174 Self::new(false, mantissa, 0, precision)
175 }
176
177 pub fn pos_infinity(precision: Precision) -> Self {
179 let mut f = Self::zero(precision);
180 f.special = SpecialValue::PosInfinity;
181 f
182 }
183
184 pub fn neg_infinity(precision: Precision) -> Self {
186 let mut f = Self::zero(precision);
187 f.special = SpecialValue::NegInfinity;
188 f
189 }
190
191 pub fn nan(precision: Precision) -> Self {
193 let mut f = Self::zero(precision);
194 f.special = SpecialValue::NaN;
195 f
196 }
197
198 pub fn is_zero(&self) -> bool {
200 self.special == SpecialValue::None && self.mantissa.is_zero()
201 }
202
203 pub fn is_pos_infinity(&self) -> bool {
205 self.special == SpecialValue::PosInfinity
206 }
207
208 pub fn is_neg_infinity(&self) -> bool {
210 self.special == SpecialValue::NegInfinity
211 }
212
213 pub fn is_infinity(&self) -> bool {
215 self.is_pos_infinity() || self.is_neg_infinity()
216 }
217
218 pub fn is_nan(&self) -> bool {
220 self.special == SpecialValue::NaN
221 }
222
223 pub fn is_finite(&self) -> bool {
225 self.special == SpecialValue::None
226 }
227
228 pub fn is_negative(&self) -> bool {
230 self.sign && !self.is_zero() && !self.is_nan()
231 }
232
233 pub fn is_positive(&self) -> bool {
235 !self.sign && !self.is_zero() && !self.is_nan()
236 }
237
238 pub fn precision(&self) -> Precision {
240 self.precision
241 }
242
243 pub fn from_f64(value: f64, precision: Precision) -> Self {
255 if value.is_nan() {
257 return Self::nan(precision);
258 }
259 if value.is_infinite() {
260 return if value > 0.0 {
261 Self::pos_infinity(precision)
262 } else {
263 Self::neg_infinity(precision)
264 };
265 }
266 if value == 0.0 {
267 let mut z = Self::zero(precision);
269 z.sign = value.is_sign_negative();
270 return z;
271 }
272
273 let bits = value.to_bits();
275 let sign = (bits >> 63) != 0;
276 let exp_bits = ((bits >> 52) & 0x7FF) as i64;
277 let mantissa_bits = bits & 0x000F_FFFF_FFFF_FFFF;
278
279 let exponent = if exp_bits == 0 {
281 1 - 1023 - 52
283 } else {
284 exp_bits - 1023 - 52
286 };
287
288 let mantissa = if exp_bits == 0 {
290 BigUint::from(mantissa_bits)
292 } else {
293 BigUint::from(mantissa_bits | (1u64 << 52))
295 };
296
297 let mut result = Self::new(sign, mantissa, exponent, precision);
299 result.normalize(RoundingMode::RoundNearest);
300 result
301 }
302
303 pub fn to_f64(&self, _rounding: RoundingMode) -> f64 {
317 if self.is_nan() {
319 return f64::NAN;
320 }
321 if self.is_pos_infinity() {
322 return f64::INFINITY;
323 }
324 if self.is_neg_infinity() {
325 return f64::NEG_INFINITY;
326 }
327 if self.is_zero() {
328 return if self.sign { -0.0 } else { 0.0 };
329 }
330
331 let precision_bits = self.precision.bits();
336 if precision_bits >= 53 {
337 let shift = (precision_bits - 53) as usize;
339 let f64_mantissa = (&self.mantissa >> shift).to_f64().unwrap_or(0.0);
340 let f64_exponent = self.exponent + shift as i64;
341
342 let result = f64_mantissa * 2.0f64.powi(f64_exponent as i32);
343 if self.sign { -result } else { result }
344 } else {
345 let mantissa_f64 = self.mantissa.to_f64().unwrap_or(0.0);
347 let result = mantissa_f64 * 2.0f64.powi(self.exponent as i32);
348 if self.sign { -result } else { result }
349 }
350 }
351
352 fn normalize(&mut self, rounding: RoundingMode) {
354 if self.mantissa.is_zero() || !self.is_finite() {
355 return;
356 }
357
358 let target_bits = self.precision.bits() as u64;
359 let current_bits = self.mantissa.bits();
360
361 if current_bits > target_bits {
362 let shift = current_bits - target_bits;
364 let (quotient, remainder) = self.mantissa.div_rem(&(BigUint::one() << shift as usize));
365
366 self.mantissa = quotient;
367 self.exponent += shift as i64;
368
369 let half = BigUint::one() << (shift as usize - 1);
371 let round_up = match rounding {
372 RoundingMode::RoundNearest => {
373 if remainder > half {
375 true
376 } else if remainder == half {
377 self.mantissa.bit(0)
379 } else {
380 false
381 }
382 }
383 RoundingMode::RoundUp => !self.sign && !remainder.is_zero(),
384 RoundingMode::RoundDown => self.sign && !remainder.is_zero(),
385 RoundingMode::RoundTowardZero => false,
386 };
387
388 if round_up {
389 self.mantissa += 1u32;
390 if self.mantissa.bits() > target_bits {
392 self.mantissa >>= 1;
393 self.exponent += 1;
394 }
395 }
396 } else if current_bits < target_bits {
397 let shift = target_bits - current_bits;
399 self.mantissa <<= shift as usize;
400 self.exponent -= shift as i64;
401 }
402 }
403
404 pub fn add(&self, other: &Self, rounding: RoundingMode) -> Self {
413 if self.is_nan() || other.is_nan() {
415 return Self::nan(self.precision.max(other.precision));
416 }
417
418 if self.is_infinity() || other.is_infinity() {
420 if self.is_pos_infinity() {
421 if other.is_neg_infinity() {
422 return Self::nan(self.precision);
423 }
424 return Self::pos_infinity(self.precision);
425 }
426 if self.is_neg_infinity() {
427 if other.is_pos_infinity() {
428 return Self::nan(self.precision);
429 }
430 return Self::neg_infinity(self.precision);
431 }
432 if other.is_pos_infinity() {
433 return Self::pos_infinity(other.precision);
434 }
435 return Self::neg_infinity(other.precision);
436 }
437
438 if self.is_zero() {
440 return other.clone();
441 }
442 if other.is_zero() {
443 return self.clone();
444 }
445
446 let result_precision = if self.precision.bits() >= other.precision.bits() {
448 self.precision
449 } else {
450 other.precision
451 };
452
453 let (m1, m2, exp, s1, s2) = self.align_with(other);
455
456 let (result_mantissa, result_sign) = if s1 == s2 {
458 (m1 + m2, s1)
460 } else {
461 match m1.cmp(&m2) {
463 Ordering::Greater => (m1 - m2, s1),
464 Ordering::Less => (m2 - m1, s2),
465 Ordering::Equal => return Self::zero(result_precision),
466 }
467 };
468
469 let mut result = Self::new(result_sign, result_mantissa, exp, result_precision);
470 result.normalize(rounding);
471 result
472 }
473
474 pub fn sub(&self, other: &Self, rounding: RoundingMode) -> Self {
476 let negated = other.neg();
477 self.add(&negated, rounding)
478 }
479
480 pub fn mul(&self, other: &Self, rounding: RoundingMode) -> Self {
482 let result_precision = if self.precision.bits() >= other.precision.bits() {
483 self.precision
484 } else {
485 other.precision
486 };
487
488 if self.is_nan() || other.is_nan() {
490 return Self::nan(result_precision);
491 }
492
493 if (self.is_zero() && other.is_infinity()) || (self.is_infinity() && other.is_zero()) {
495 return Self::nan(result_precision);
496 }
497
498 let result_sign = self.sign != other.sign;
500 if self.is_infinity() || other.is_infinity() {
501 return if result_sign {
502 Self::neg_infinity(result_precision)
503 } else {
504 Self::pos_infinity(result_precision)
505 };
506 }
507
508 if self.is_zero() || other.is_zero() {
510 let mut z = Self::zero(result_precision);
511 z.sign = result_sign;
512 return z;
513 }
514
515 let result_mantissa = &self.mantissa * &other.mantissa;
518 let result_exponent = self.exponent + other.exponent;
519
520 let mut result = Self::new(
521 result_sign,
522 result_mantissa,
523 result_exponent,
524 result_precision,
525 );
526 result.normalize(rounding);
527 result
528 }
529
530 pub fn div(&self, other: &Self, rounding: RoundingMode) -> Self {
532 let result_precision = if self.precision.bits() >= other.precision.bits() {
533 self.precision
534 } else {
535 other.precision
536 };
537
538 if self.is_nan() || other.is_nan() {
540 return Self::nan(result_precision);
541 }
542
543 let result_sign = self.sign != other.sign;
544
545 if (self.is_zero() && other.is_zero()) || (self.is_infinity() && other.is_infinity()) {
547 return Self::nan(result_precision);
548 }
549
550 if other.is_zero() {
552 return if result_sign {
553 Self::neg_infinity(result_precision)
554 } else {
555 Self::pos_infinity(result_precision)
556 };
557 }
558
559 if self.is_zero() {
561 let mut z = Self::zero(result_precision);
562 z.sign = result_sign;
563 return z;
564 }
565
566 if other.is_infinity() {
568 let mut z = Self::zero(result_precision);
569 z.sign = result_sign;
570 return z;
571 }
572
573 if self.is_infinity() {
575 return if result_sign {
576 Self::neg_infinity(result_precision)
577 } else {
578 Self::pos_infinity(result_precision)
579 };
580 }
581
582 let extra_bits = result_precision.bits() as usize + 10; let shifted_dividend = &self.mantissa << extra_bits;
585 let result_mantissa = &shifted_dividend / &other.mantissa;
586 let result_exponent = self.exponent - other.exponent - (extra_bits as i64);
591
592 let mut result = Self::new(
593 result_sign,
594 result_mantissa,
595 result_exponent,
596 result_precision,
597 );
598 result.normalize(rounding);
599 result
600 }
601
602 pub fn sqrt(&self, rounding: RoundingMode) -> Self {
606 if self.is_nan() || self.is_neg_infinity() || (self.is_negative() && !self.is_zero()) {
608 return Self::nan(self.precision);
609 }
610 if self.is_pos_infinity() {
611 return Self::pos_infinity(self.precision);
612 }
613 if self.is_zero() {
614 return Self::zero(self.precision);
615 }
616
617 let initial_guess = self.to_f64(RoundingMode::RoundNearest).sqrt();
620 let mut x = Self::from_f64(initial_guess, self.precision);
621
622 let two = Self::from_f64(2.0, self.precision);
624 let tolerance_bits = self.precision.bits() + 10;
625
626 for _ in 0..100 {
627 let s_div_x = Self::div(self, &x, rounding);
629 let sum = Self::add(&x, &s_div_x, rounding);
630 let x_new = Self::div(&sum, &two, rounding);
631
632 let diff = Self::sub(&x_new, &x, rounding);
634 if diff.is_zero()
635 || (diff.mantissa.bits() as i64 + diff.exponent
636 < x_new.exponent - tolerance_bits as i64)
637 {
638 return x_new;
639 }
640
641 x = x_new;
642 }
643
644 x
645 }
646
647 pub fn neg(&self) -> Self {
649 if self.is_nan() {
650 return Self::nan(self.precision);
651 }
652 if self.is_pos_infinity() {
653 return Self::neg_infinity(self.precision);
654 }
655 if self.is_neg_infinity() {
656 return Self::pos_infinity(self.precision);
657 }
658
659 let mut result = self.clone();
660 result.sign = !result.sign;
661 result
662 }
663
664 pub fn abs(&self) -> Self {
666 if self.is_nan() {
667 return Self::nan(self.precision);
668 }
669 if self.is_neg_infinity() {
670 return Self::pos_infinity(self.precision);
671 }
672
673 let mut result = self.clone();
674 result.sign = false;
675 result
676 }
677
678 fn align_with(&self, other: &Self) -> (BigUint, BigUint, i64, bool, bool) {
681 let exp_diff = self.exponent - other.exponent;
682
683 if exp_diff >= 0 {
684 let shifted_other = &other.mantissa >> (exp_diff as usize);
686 (
687 self.mantissa.clone(),
688 shifted_other,
689 self.exponent,
690 self.sign,
691 other.sign,
692 )
693 } else {
694 let shift = (-exp_diff) as usize;
696 let shifted_self = &self.mantissa >> shift;
697 (
698 shifted_self,
699 other.mantissa.clone(),
700 other.exponent,
701 self.sign,
702 other.sign,
703 )
704 }
705 }
706
707 pub fn partial_compare(&self, other: &Self) -> Option<Ordering> {
711 if self.is_nan() || other.is_nan() {
713 return None;
714 }
715
716 if self.is_pos_infinity() {
718 return if other.is_pos_infinity() {
719 Some(Ordering::Equal)
720 } else {
721 Some(Ordering::Greater)
722 };
723 }
724 if self.is_neg_infinity() {
725 return if other.is_neg_infinity() {
726 Some(Ordering::Equal)
727 } else {
728 Some(Ordering::Less)
729 };
730 }
731 if other.is_pos_infinity() {
732 return Some(Ordering::Less);
733 }
734 if other.is_neg_infinity() {
735 return Some(Ordering::Greater);
736 }
737
738 let self_zero = self.is_zero();
740 let other_zero = other.is_zero();
741 if self_zero && other_zero {
742 return Some(Ordering::Equal);
743 }
744 if self_zero {
745 return if other.sign {
746 Some(Ordering::Greater)
747 } else {
748 Some(Ordering::Less)
749 };
750 }
751 if other_zero {
752 return if self.sign {
753 Some(Ordering::Less)
754 } else {
755 Some(Ordering::Greater)
756 };
757 }
758
759 if self.sign != other.sign {
761 return if self.sign {
762 Some(Ordering::Less)
763 } else {
764 Some(Ordering::Greater)
765 };
766 }
767
768 let (m1, m2, _, _, _) = self.align_with(other);
770 let mag_cmp = m1.cmp(&m2);
771
772 if self.sign {
774 Some(mag_cmp.reverse())
775 } else {
776 Some(mag_cmp)
777 }
778 }
779
780 pub fn from_str(s: &str, precision: Precision) -> Option<Self> {
784 let s = s.trim();
785
786 if s.eq_ignore_ascii_case("nan") {
788 return Some(Self::nan(precision));
789 }
790 if s.eq_ignore_ascii_case("inf") || s.eq_ignore_ascii_case("+inf") {
791 return Some(Self::pos_infinity(precision));
792 }
793 if s.eq_ignore_ascii_case("-inf") {
794 return Some(Self::neg_infinity(precision));
795 }
796
797 if let Ok(f) = s.parse::<f64>() {
799 return Some(Self::from_f64(f, precision));
800 }
801
802 None
803 }
804}
805
806impl fmt::Debug for ArbitraryFloat {
807 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
808 if self.is_nan() {
809 write!(f, "NaN")
810 } else if self.is_pos_infinity() {
811 write!(f, "+Inf")
812 } else if self.is_neg_infinity() {
813 write!(f, "-Inf")
814 } else {
815 write!(
816 f,
817 "ArbitraryFloat {{ sign: {}, mantissa: {}, exp: {}, prec: {} }}",
818 self.sign,
819 self.mantissa,
820 self.exponent,
821 self.precision.bits()
822 )
823 }
824 }
825}
826
827impl fmt::Display for ArbitraryFloat {
828 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
829 if self.is_nan() {
830 write!(f, "NaN")
831 } else if self.is_pos_infinity() {
832 write!(f, "+Inf")
833 } else if self.is_neg_infinity() {
834 write!(f, "-Inf")
835 } else {
836 let value = self.to_f64(RoundingMode::RoundNearest);
838 if self.sign && !value.is_sign_negative() {
839 write!(f, "-{}", value.abs())
840 } else {
841 write!(f, "{}", value)
842 }
843 }
844 }
845}
846
847impl PartialEq for ArbitraryFloat {
848 fn eq(&self, other: &Self) -> bool {
849 self.partial_compare(other) == Some(Ordering::Equal)
850 }
851}
852
853impl PartialOrd for ArbitraryFloat {
854 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
855 self.partial_compare(other)
856 }
857}
858
859impl Add for ArbitraryFloat {
860 type Output = Self;
861
862 fn add(self, rhs: Self) -> Self::Output {
863 ArbitraryFloat::add(&self, &rhs, RoundingMode::RoundNearest)
864 }
865}
866
867impl Add for &ArbitraryFloat {
868 type Output = ArbitraryFloat;
869
870 fn add(self, rhs: Self) -> Self::Output {
871 ArbitraryFloat::add(self, rhs, RoundingMode::RoundNearest)
872 }
873}
874
875impl Sub for ArbitraryFloat {
876 type Output = Self;
877
878 fn sub(self, rhs: Self) -> Self::Output {
879 ArbitraryFloat::sub(&self, &rhs, RoundingMode::RoundNearest)
880 }
881}
882
883impl Sub for &ArbitraryFloat {
884 type Output = ArbitraryFloat;
885
886 fn sub(self, rhs: Self) -> Self::Output {
887 ArbitraryFloat::sub(self, rhs, RoundingMode::RoundNearest)
888 }
889}
890
891impl Mul for ArbitraryFloat {
892 type Output = Self;
893
894 fn mul(self, rhs: Self) -> Self::Output {
895 ArbitraryFloat::mul(&self, &rhs, RoundingMode::RoundNearest)
896 }
897}
898
899impl Mul for &ArbitraryFloat {
900 type Output = ArbitraryFloat;
901
902 fn mul(self, rhs: Self) -> Self::Output {
903 ArbitraryFloat::mul(self, rhs, RoundingMode::RoundNearest)
904 }
905}
906
907impl Div for ArbitraryFloat {
908 type Output = Self;
909
910 fn div(self, rhs: Self) -> Self::Output {
911 ArbitraryFloat::div(&self, &rhs, RoundingMode::RoundNearest)
912 }
913}
914
915impl Div for &ArbitraryFloat {
916 type Output = ArbitraryFloat;
917
918 fn div(self, rhs: Self) -> Self::Output {
919 ArbitraryFloat::div(self, rhs, RoundingMode::RoundNearest)
920 }
921}
922
923impl Neg for ArbitraryFloat {
924 type Output = Self;
925
926 fn neg(self) -> Self::Output {
927 ArbitraryFloat::neg(&self)
928 }
929}
930
931impl Neg for &ArbitraryFloat {
932 type Output = ArbitraryFloat;
933
934 fn neg(self) -> Self::Output {
935 ArbitraryFloat::neg(self)
936 }
937}
938
939impl Precision {
941 fn max(self, other: Self) -> Self {
943 if self.bits >= other.bits { self } else { other }
944 }
945}
946
947#[derive(Debug, Clone)]
952pub struct ArbitraryFloatContext {
953 pub precision: Precision,
955 pub rounding: RoundingMode,
957}
958
959impl ArbitraryFloatContext {
960 pub fn new(precision: Precision, rounding: RoundingMode) -> Self {
962 Self {
963 precision,
964 rounding,
965 }
966 }
967
968 pub fn zero(&self) -> ArbitraryFloat {
970 ArbitraryFloat::zero(self.precision)
971 }
972
973 pub fn one(&self) -> ArbitraryFloat {
975 ArbitraryFloat::one(self.precision)
976 }
977
978 pub fn from_f64(&self, value: f64) -> ArbitraryFloat {
980 ArbitraryFloat::from_f64(value, self.precision)
981 }
982
983 pub fn add(&self, a: &ArbitraryFloat, b: &ArbitraryFloat) -> ArbitraryFloat {
985 a.add(b, self.rounding)
986 }
987
988 pub fn sub(&self, a: &ArbitraryFloat, b: &ArbitraryFloat) -> ArbitraryFloat {
990 a.sub(b, self.rounding)
991 }
992
993 pub fn mul(&self, a: &ArbitraryFloat, b: &ArbitraryFloat) -> ArbitraryFloat {
995 a.mul(b, self.rounding)
996 }
997
998 pub fn div(&self, a: &ArbitraryFloat, b: &ArbitraryFloat) -> ArbitraryFloat {
1000 a.div(b, self.rounding)
1001 }
1002
1003 pub fn sqrt(&self, a: &ArbitraryFloat) -> ArbitraryFloat {
1005 a.sqrt(self.rounding)
1006 }
1007}
1008
1009impl Default for ArbitraryFloatContext {
1010 fn default() -> Self {
1011 Self::new(Precision::DOUBLE, RoundingMode::RoundNearest)
1012 }
1013}
1014
1015#[cfg(test)]
1016mod tests {
1017 use super::*;
1018
1019 const EPSILON: f64 = 1e-10;
1020
1021 fn approx_eq(a: f64, b: f64) -> bool {
1022 (a - b).abs() < EPSILON || (a.is_nan() && b.is_nan())
1023 }
1024
1025 #[test]
1026 fn test_precision_constants() {
1027 assert_eq!(Precision::DOUBLE.bits(), 53);
1028 assert_eq!(Precision::EXTENDED.bits(), 64);
1029 assert_eq!(Precision::QUAD.bits(), 113);
1030 assert_eq!(Precision::HIGH.bits(), 256);
1031 }
1032
1033 #[test]
1034 fn test_from_f64_basic() {
1035 let prec = Precision::new(64);
1036 let f = ArbitraryFloat::from_f64(core::f64::consts::PI, prec);
1037 let back = f.to_f64(RoundingMode::RoundNearest);
1038 assert!(approx_eq(back, core::f64::consts::PI));
1039 }
1040
1041 #[test]
1042 fn test_from_f64_special_values() {
1043 let prec = Precision::new(64);
1044
1045 let nan = ArbitraryFloat::from_f64(f64::NAN, prec);
1046 assert!(nan.is_nan());
1047
1048 let pos_inf = ArbitraryFloat::from_f64(f64::INFINITY, prec);
1049 assert!(pos_inf.is_pos_infinity());
1050
1051 let neg_inf = ArbitraryFloat::from_f64(f64::NEG_INFINITY, prec);
1052 assert!(neg_inf.is_neg_infinity());
1053
1054 let zero = ArbitraryFloat::from_f64(0.0, prec);
1055 assert!(zero.is_zero());
1056 }
1057
1058 #[test]
1059 fn test_addition() {
1060 let prec = Precision::new(64);
1061 let a = ArbitraryFloat::from_f64(1.5, prec);
1062 let b = ArbitraryFloat::from_f64(2.5, prec);
1063 let sum = ArbitraryFloat::add(&a, &b, RoundingMode::RoundNearest);
1064 assert!(approx_eq(sum.to_f64(RoundingMode::RoundNearest), 4.0));
1065 }
1066
1067 #[test]
1068 fn test_subtraction() {
1069 let prec = Precision::new(64);
1070 let a = ArbitraryFloat::from_f64(5.0, prec);
1071 let b = ArbitraryFloat::from_f64(3.0, prec);
1072 let diff = ArbitraryFloat::sub(&a, &b, RoundingMode::RoundNearest);
1073 assert!(approx_eq(diff.to_f64(RoundingMode::RoundNearest), 2.0));
1074 }
1075
1076 #[test]
1077 fn test_multiplication() {
1078 let prec = Precision::new(64);
1079 let a = ArbitraryFloat::from_f64(3.0, prec);
1080 let b = ArbitraryFloat::from_f64(4.0, prec);
1081 let prod = ArbitraryFloat::mul(&a, &b, RoundingMode::RoundNearest);
1082 assert!(approx_eq(prod.to_f64(RoundingMode::RoundNearest), 12.0));
1083 }
1084
1085 #[test]
1086 fn test_division() {
1087 let prec = Precision::new(64);
1088 let a = ArbitraryFloat::from_f64(10.0, prec);
1089 let b = ArbitraryFloat::from_f64(4.0, prec);
1090 let quot = ArbitraryFloat::div(&a, &b, RoundingMode::RoundNearest);
1091 assert!(approx_eq(quot.to_f64(RoundingMode::RoundNearest), 2.5));
1092 }
1093
1094 #[test]
1095 fn test_sqrt() {
1096 let prec = Precision::new(64);
1097 let a = ArbitraryFloat::from_f64(4.0, prec);
1098 let root = a.sqrt(RoundingMode::RoundNearest);
1099 assert!(approx_eq(root.to_f64(RoundingMode::RoundNearest), 2.0));
1100 }
1101
1102 #[test]
1103 fn test_sqrt_2() {
1104 let prec = Precision::new(128);
1105 let a = ArbitraryFloat::from_f64(2.0, prec);
1106 let root = a.sqrt(RoundingMode::RoundNearest);
1107 let expected = 2.0f64.sqrt();
1108 let result = root.to_f64(RoundingMode::RoundNearest);
1109 assert!(
1110 (result - expected).abs() < 1e-14,
1111 "sqrt(2) = {}, expected {}",
1112 result,
1113 expected
1114 );
1115 }
1116
1117 #[test]
1118 fn test_negation() {
1119 let prec = Precision::new(64);
1120 let a = ArbitraryFloat::from_f64(5.0, prec);
1121 let neg_a = a.neg();
1122 assert!(approx_eq(neg_a.to_f64(RoundingMode::RoundNearest), -5.0));
1123 }
1124
1125 #[test]
1126 fn test_abs() {
1127 let prec = Precision::new(64);
1128 let a = ArbitraryFloat::from_f64(-5.0, prec);
1129 let abs_a = a.abs();
1130 assert!(approx_eq(abs_a.to_f64(RoundingMode::RoundNearest), 5.0));
1131 }
1132
1133 #[test]
1134 fn test_comparison() {
1135 let prec = Precision::new(64);
1136 let a = ArbitraryFloat::from_f64(3.0, prec);
1137 let b = ArbitraryFloat::from_f64(5.0, prec);
1138 let c = ArbitraryFloat::from_f64(3.0, prec);
1139
1140 assert!(a < b);
1141 assert!(b > a);
1142 assert!(a == c);
1143 assert!(a <= c);
1144 assert!(a >= c);
1145 }
1146
1147 #[test]
1148 fn test_comparison_with_nan() {
1149 let prec = Precision::new(64);
1150 let a = ArbitraryFloat::from_f64(3.0, prec);
1151 let nan = ArbitraryFloat::nan(prec);
1152
1153 assert!(a.partial_compare(&nan).is_none());
1154 assert!(nan.partial_compare(&a).is_none());
1155 assert!(nan.partial_compare(&nan).is_none());
1156 }
1157
1158 #[test]
1159 fn test_infinity_operations() {
1160 let prec = Precision::new(64);
1161 let pos_inf = ArbitraryFloat::pos_infinity(prec);
1162 let neg_inf = ArbitraryFloat::neg_infinity(prec);
1163 let one = ArbitraryFloat::from_f64(1.0, prec);
1164
1165 let sum = ArbitraryFloat::add(&pos_inf, &one, RoundingMode::RoundNearest);
1167 assert!(sum.is_pos_infinity());
1168
1169 let sum2 = ArbitraryFloat::add(&pos_inf, &neg_inf, RoundingMode::RoundNearest);
1171 assert!(sum2.is_nan());
1172
1173 let two = ArbitraryFloat::from_f64(2.0, prec);
1175 let prod = ArbitraryFloat::mul(&pos_inf, &two, RoundingMode::RoundNearest);
1176 assert!(prod.is_pos_infinity());
1177
1178 let zero = ArbitraryFloat::zero(prec);
1180 let prod2 = ArbitraryFloat::mul(&pos_inf, &zero, RoundingMode::RoundNearest);
1181 assert!(prod2.is_nan());
1182 }
1183
1184 #[test]
1185 fn test_zero_operations() {
1186 let prec = Precision::new(64);
1187 let zero = ArbitraryFloat::zero(prec);
1188 let one = ArbitraryFloat::from_f64(1.0, prec);
1189
1190 let sum = ArbitraryFloat::add(&zero, &one, RoundingMode::RoundNearest);
1192 assert!(approx_eq(sum.to_f64(RoundingMode::RoundNearest), 1.0));
1193
1194 let prod = ArbitraryFloat::mul(&zero, &one, RoundingMode::RoundNearest);
1196 assert!(prod.is_zero());
1197
1198 let quot = ArbitraryFloat::div(&one, &zero, RoundingMode::RoundNearest);
1200 assert!(quot.is_pos_infinity());
1201
1202 let quot2 = ArbitraryFloat::div(&zero, &zero, RoundingMode::RoundNearest);
1204 assert!(quot2.is_nan());
1205 }
1206
1207 #[test]
1208 fn test_operator_overloads() {
1209 let prec = Precision::new(64);
1210 let a = ArbitraryFloat::from_f64(10.0, prec);
1211 let b = ArbitraryFloat::from_f64(3.0, prec);
1212
1213 let sum = a.clone() + b.clone();
1214 assert!(approx_eq(sum.to_f64(RoundingMode::RoundNearest), 13.0));
1215
1216 let diff = a.clone() - b.clone();
1217 assert!(approx_eq(diff.to_f64(RoundingMode::RoundNearest), 7.0));
1218
1219 let prod = a.clone() * b.clone();
1220 assert!(approx_eq(prod.to_f64(RoundingMode::RoundNearest), 30.0));
1221
1222 let quot = a.clone() / b.clone();
1223 let result = quot.to_f64(RoundingMode::RoundNearest);
1224 assert!(
1225 (result - 10.0 / 3.0).abs() < 1e-10,
1226 "10/3 = {}, expected {}",
1227 result,
1228 10.0 / 3.0
1229 );
1230
1231 let neg = -a;
1232 assert!(approx_eq(neg.to_f64(RoundingMode::RoundNearest), -10.0));
1233 }
1234
1235 #[test]
1236 fn test_context() {
1237 let ctx = ArbitraryFloatContext::new(Precision::new(128), RoundingMode::RoundNearest);
1238
1239 let a = ctx.from_f64(3.5);
1240 let b = ctx.from_f64(2.25);
1241
1242 let sum = ctx.add(&a, &b);
1243 assert!(approx_eq(
1244 sum.to_f64(RoundingMode::RoundNearest),
1245 3.5 + 2.25
1246 ));
1247
1248 let sqrt_2 = ctx.sqrt(&ctx.from_f64(2.0));
1249 assert!((sqrt_2.to_f64(RoundingMode::RoundNearest) - 2.0f64.sqrt()).abs() < 1e-14);
1250 }
1251
1252 #[test]
1253 fn test_from_str() {
1254 let prec = Precision::new(64);
1255
1256 let nan = ArbitraryFloat::from_str("NaN", prec).expect("serialization failed");
1257 assert!(nan.is_nan());
1258
1259 let inf = ArbitraryFloat::from_str("inf", prec).expect("serialization failed");
1260 assert!(inf.is_pos_infinity());
1261
1262 let neg_inf = ArbitraryFloat::from_str("-inf", prec).expect("serialization failed");
1263 assert!(neg_inf.is_neg_infinity());
1264
1265 let val = ArbitraryFloat::from_str("3.5", prec).expect("serialization failed");
1266 assert!(approx_eq(val.to_f64(RoundingMode::RoundNearest), 3.5));
1267 }
1268
1269 #[test]
1270 fn test_high_precision() {
1271 let low_prec = Precision::new(53);
1273 let high_prec = Precision::new(256);
1274
1275 let one_low = ArbitraryFloat::from_f64(1.0, low_prec);
1277 let three_low = ArbitraryFloat::from_f64(3.0, low_prec);
1278 let div_low = ArbitraryFloat::div(&one_low, &three_low, RoundingMode::RoundNearest);
1279 let result_low = ArbitraryFloat::mul(&div_low, &three_low, RoundingMode::RoundNearest);
1280
1281 let one_high = ArbitraryFloat::from_f64(1.0, high_prec);
1282 let three_high = ArbitraryFloat::from_f64(3.0, high_prec);
1283 let div_high = ArbitraryFloat::div(&one_high, &three_high, RoundingMode::RoundNearest);
1284 let result_high = ArbitraryFloat::mul(&div_high, &three_high, RoundingMode::RoundNearest);
1285
1286 let error_low = (result_low.to_f64(RoundingMode::RoundNearest) - 1.0).abs();
1288 let error_high = (result_high.to_f64(RoundingMode::RoundNearest) - 1.0).abs();
1289
1290 assert!(
1291 error_high <= error_low + 1e-15,
1292 "High precision error {} should be <= low precision error {}",
1293 error_high,
1294 error_low
1295 );
1296 }
1297
1298 #[test]
1299 fn test_display() {
1300 let prec = Precision::new(64);
1301
1302 let nan = ArbitraryFloat::nan(prec);
1303 assert_eq!(format!("{}", nan), "NaN");
1304
1305 let pos_inf = ArbitraryFloat::pos_infinity(prec);
1306 assert_eq!(format!("{}", pos_inf), "+Inf");
1307
1308 let neg_inf = ArbitraryFloat::neg_infinity(prec);
1309 assert_eq!(format!("{}", neg_inf), "-Inf");
1310 }
1311}