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)]
141pub struct ArbitraryFloat {
142 sign: bool,
144 mantissa: BigUint,
146 exponent: i64,
148 precision: Precision,
150 special: SpecialValue,
152}
153
154fn shift_right_sticky(value: &BigUint, shift: usize) -> BigUint {
167 if shift == 0 {
168 return value.clone();
169 }
170 let value_bits = value.bits() as usize;
171 if shift >= value_bits {
172 return if value.is_zero() {
175 BigUint::zero()
176 } else {
177 BigUint::one()
178 };
179 }
180 let shifted = value >> shift;
181 let mask = (BigUint::one() << shift) - BigUint::one();
182 let dropped = value & &mask;
183 if dropped.is_zero() {
184 shifted
185 } else {
186 shifted | BigUint::one()
187 }
188}
189
190impl ArbitraryFloat {
191 fn new(sign: bool, mantissa: BigUint, exponent: i64, precision: Precision) -> Self {
199 Self {
200 sign,
201 mantissa,
202 exponent,
203 precision,
204 special: SpecialValue::None,
205 }
206 }
207
208 pub fn zero(precision: Precision) -> Self {
210 Self::new(false, BigUint::zero(), 0, precision)
211 }
212
213 pub fn one(precision: Precision) -> Self {
215 let mantissa = BigUint::one() << (precision.bits() - 1);
221 let exponent = 1 - precision.bits() as i64;
222 Self::new(false, mantissa, exponent, precision)
223 }
224
225 pub fn pos_infinity(precision: Precision) -> Self {
227 let mut f = Self::zero(precision);
228 f.special = SpecialValue::PosInfinity;
229 f
230 }
231
232 pub fn neg_infinity(precision: Precision) -> Self {
234 let mut f = Self::zero(precision);
235 f.special = SpecialValue::NegInfinity;
236 f
237 }
238
239 pub fn nan(precision: Precision) -> Self {
241 let mut f = Self::zero(precision);
242 f.special = SpecialValue::NaN;
243 f
244 }
245
246 pub fn is_zero(&self) -> bool {
248 self.special == SpecialValue::None && self.mantissa.is_zero()
249 }
250
251 pub fn is_pos_infinity(&self) -> bool {
253 self.special == SpecialValue::PosInfinity
254 }
255
256 pub fn is_neg_infinity(&self) -> bool {
258 self.special == SpecialValue::NegInfinity
259 }
260
261 pub fn is_infinity(&self) -> bool {
263 self.is_pos_infinity() || self.is_neg_infinity()
264 }
265
266 pub fn is_nan(&self) -> bool {
268 self.special == SpecialValue::NaN
269 }
270
271 pub fn is_finite(&self) -> bool {
273 self.special == SpecialValue::None
274 }
275
276 pub fn is_negative(&self) -> bool {
278 self.sign && !self.is_zero() && !self.is_nan()
279 }
280
281 pub fn is_positive(&self) -> bool {
283 !self.sign && !self.is_zero() && !self.is_nan()
284 }
285
286 pub fn precision(&self) -> Precision {
288 self.precision
289 }
290
291 pub fn from_f64(value: f64, precision: Precision) -> Self {
303 if value.is_nan() {
305 return Self::nan(precision);
306 }
307 if value.is_infinite() {
308 return if value > 0.0 {
309 Self::pos_infinity(precision)
310 } else {
311 Self::neg_infinity(precision)
312 };
313 }
314 if value == 0.0 {
315 let mut z = Self::zero(precision);
317 z.sign = value.is_sign_negative();
318 return z;
319 }
320
321 let bits = value.to_bits();
323 let sign = (bits >> 63) != 0;
324 let exp_bits = ((bits >> 52) & 0x7FF) as i64;
325 let mantissa_bits = bits & 0x000F_FFFF_FFFF_FFFF;
326
327 let exponent = if exp_bits == 0 {
329 1 - 1023 - 52
331 } else {
332 exp_bits - 1023 - 52
334 };
335
336 let mantissa = if exp_bits == 0 {
338 BigUint::from(mantissa_bits)
340 } else {
341 BigUint::from(mantissa_bits | (1u64 << 52))
343 };
344
345 let mut result = Self::new(sign, mantissa, exponent, precision);
347 result.normalize(RoundingMode::RoundNearest);
348 result
349 }
350
351 pub fn to_f64(&self, _rounding: RoundingMode) -> f64 {
365 if self.is_nan() {
367 return f64::NAN;
368 }
369 if self.is_pos_infinity() {
370 return f64::INFINITY;
371 }
372 if self.is_neg_infinity() {
373 return f64::NEG_INFINITY;
374 }
375 if self.is_zero() {
376 return if self.sign { -0.0 } else { 0.0 };
377 }
378
379 let precision_bits = self.precision.bits();
384 if precision_bits >= 53 {
385 let shift = (precision_bits - 53) as usize;
387 let f64_mantissa = (&self.mantissa >> shift).to_f64().unwrap_or(0.0);
388 let f64_exponent = self.exponent + shift as i64;
389
390 let result = f64_mantissa * 2.0f64.powi(f64_exponent as i32);
391 if self.sign { -result } else { result }
392 } else {
393 let mantissa_f64 = self.mantissa.to_f64().unwrap_or(0.0);
395 let result = mantissa_f64 * 2.0f64.powi(self.exponent as i32);
396 if self.sign { -result } else { result }
397 }
398 }
399
400 fn normalize(&mut self, rounding: RoundingMode) {
402 if self.mantissa.is_zero() || !self.is_finite() {
403 return;
404 }
405
406 let target_bits = self.precision.bits() as u64;
407 let current_bits = self.mantissa.bits();
408
409 if current_bits > target_bits {
410 let shift = current_bits - target_bits;
412 let (quotient, remainder) = self.mantissa.div_rem(&(BigUint::one() << shift as usize));
413
414 self.mantissa = quotient;
415 self.exponent += shift as i64;
416
417 let half = BigUint::one() << (shift as usize - 1);
419 let round_up = match rounding {
420 RoundingMode::RoundNearest => {
421 if remainder > half {
423 true
424 } else if remainder == half {
425 self.mantissa.bit(0)
427 } else {
428 false
429 }
430 }
431 RoundingMode::RoundUp => !self.sign && !remainder.is_zero(),
432 RoundingMode::RoundDown => self.sign && !remainder.is_zero(),
433 RoundingMode::RoundTowardZero => false,
434 };
435
436 if round_up {
437 self.mantissa += 1u32;
438 if self.mantissa.bits() > target_bits {
440 self.mantissa >>= 1;
441 self.exponent += 1;
442 }
443 }
444 } else if current_bits < target_bits {
445 let shift = target_bits - current_bits;
447 self.mantissa <<= shift as usize;
448 self.exponent -= shift as i64;
449 }
450 }
451
452 pub fn add(&self, other: &Self, rounding: RoundingMode) -> Self {
461 if self.is_nan() || other.is_nan() {
463 return Self::nan(self.precision.max(other.precision));
464 }
465
466 if self.is_infinity() || other.is_infinity() {
468 if self.is_pos_infinity() {
469 if other.is_neg_infinity() {
470 return Self::nan(self.precision);
471 }
472 return Self::pos_infinity(self.precision);
473 }
474 if self.is_neg_infinity() {
475 if other.is_pos_infinity() {
476 return Self::nan(self.precision);
477 }
478 return Self::neg_infinity(self.precision);
479 }
480 if other.is_pos_infinity() {
481 return Self::pos_infinity(other.precision);
482 }
483 return Self::neg_infinity(other.precision);
484 }
485
486 if self.is_zero() {
488 return other.clone();
489 }
490 if other.is_zero() {
491 return self.clone();
492 }
493
494 let result_precision = if self.precision.bits() >= other.precision.bits() {
496 self.precision
497 } else {
498 other.precision
499 };
500
501 let (m1, m2, exp, s1, s2) = self.align_with(other);
503
504 let (result_mantissa, result_sign) = if s1 == s2 {
506 (m1 + m2, s1)
508 } else {
509 match m1.cmp(&m2) {
511 Ordering::Greater => (m1 - m2, s1),
512 Ordering::Less => (m2 - m1, s2),
513 Ordering::Equal => return Self::zero(result_precision),
514 }
515 };
516
517 let mut result = Self::new(result_sign, result_mantissa, exp, result_precision);
518 result.normalize(rounding);
519 result
520 }
521
522 pub fn sub(&self, other: &Self, rounding: RoundingMode) -> Self {
524 let negated = other.neg();
525 self.add(&negated, rounding)
526 }
527
528 pub fn mul(&self, other: &Self, rounding: RoundingMode) -> Self {
530 let result_precision = if self.precision.bits() >= other.precision.bits() {
531 self.precision
532 } else {
533 other.precision
534 };
535
536 if self.is_nan() || other.is_nan() {
538 return Self::nan(result_precision);
539 }
540
541 if (self.is_zero() && other.is_infinity()) || (self.is_infinity() && other.is_zero()) {
543 return Self::nan(result_precision);
544 }
545
546 let result_sign = self.sign != other.sign;
548 if self.is_infinity() || other.is_infinity() {
549 return if result_sign {
550 Self::neg_infinity(result_precision)
551 } else {
552 Self::pos_infinity(result_precision)
553 };
554 }
555
556 if self.is_zero() || other.is_zero() {
558 let mut z = Self::zero(result_precision);
559 z.sign = result_sign;
560 return z;
561 }
562
563 let result_mantissa = &self.mantissa * &other.mantissa;
566 let result_exponent = self.exponent + other.exponent;
567
568 let mut result = Self::new(
569 result_sign,
570 result_mantissa,
571 result_exponent,
572 result_precision,
573 );
574 result.normalize(rounding);
575 result
576 }
577
578 pub fn div(&self, other: &Self, rounding: RoundingMode) -> Self {
580 let result_precision = if self.precision.bits() >= other.precision.bits() {
581 self.precision
582 } else {
583 other.precision
584 };
585
586 if self.is_nan() || other.is_nan() {
588 return Self::nan(result_precision);
589 }
590
591 let result_sign = self.sign != other.sign;
592
593 if (self.is_zero() && other.is_zero()) || (self.is_infinity() && other.is_infinity()) {
595 return Self::nan(result_precision);
596 }
597
598 if other.is_zero() {
600 return if result_sign {
601 Self::neg_infinity(result_precision)
602 } else {
603 Self::pos_infinity(result_precision)
604 };
605 }
606
607 if self.is_zero() {
609 let mut z = Self::zero(result_precision);
610 z.sign = result_sign;
611 return z;
612 }
613
614 if other.is_infinity() {
616 let mut z = Self::zero(result_precision);
617 z.sign = result_sign;
618 return z;
619 }
620
621 if self.is_infinity() {
623 return if result_sign {
624 Self::neg_infinity(result_precision)
625 } else {
626 Self::pos_infinity(result_precision)
627 };
628 }
629
630 let extra_bits = result_precision.bits() as usize + 10; let shifted_dividend = &self.mantissa << extra_bits;
633 let result_mantissa = &shifted_dividend / &other.mantissa;
634 let result_exponent = self.exponent - other.exponent - (extra_bits as i64);
639
640 let mut result = Self::new(
641 result_sign,
642 result_mantissa,
643 result_exponent,
644 result_precision,
645 );
646 result.normalize(rounding);
647 result
648 }
649
650 pub fn sqrt(&self, rounding: RoundingMode) -> Self {
654 if self.is_nan() || self.is_neg_infinity() || (self.is_negative() && !self.is_zero()) {
656 return Self::nan(self.precision);
657 }
658 if self.is_pos_infinity() {
659 return Self::pos_infinity(self.precision);
660 }
661 if self.is_zero() {
662 return Self::zero(self.precision);
663 }
664
665 let initial_guess = self.to_f64(RoundingMode::RoundNearest).sqrt();
668 let mut x = Self::from_f64(initial_guess, self.precision);
669
670 let two = Self::from_f64(2.0, self.precision);
672 let tolerance_bits = self.precision.bits() + 10;
673
674 for _ in 0..100 {
675 let s_div_x = Self::div(self, &x, rounding);
677 let sum = Self::add(&x, &s_div_x, rounding);
678 let x_new = Self::div(&sum, &two, rounding);
679
680 let diff = Self::sub(&x_new, &x, rounding);
682 if diff.is_zero()
683 || (diff.mantissa.bits() as i64 + diff.exponent
684 < x_new.exponent - tolerance_bits as i64)
685 {
686 return x_new;
687 }
688
689 x = x_new;
690 }
691
692 x
693 }
694
695 pub fn neg(&self) -> Self {
697 if self.is_nan() {
698 return Self::nan(self.precision);
699 }
700 if self.is_pos_infinity() {
701 return Self::neg_infinity(self.precision);
702 }
703 if self.is_neg_infinity() {
704 return Self::pos_infinity(self.precision);
705 }
706
707 let mut result = self.clone();
708 result.sign = !result.sign;
709 result
710 }
711
712 pub fn abs(&self) -> Self {
714 if self.is_nan() {
715 return Self::nan(self.precision);
716 }
717 if self.is_neg_infinity() {
718 return Self::pos_infinity(self.precision);
719 }
720
721 let mut result = self.clone();
722 result.sign = false;
723 result
724 }
725
726 fn align_with(&self, other: &Self) -> (BigUint, BigUint, i64, bool, bool) {
729 let exp_diff = self.exponent - other.exponent;
730
731 if exp_diff >= 0 {
732 let shifted_other = shift_right_sticky(&other.mantissa, exp_diff as usize);
734 (
735 self.mantissa.clone(),
736 shifted_other,
737 self.exponent,
738 self.sign,
739 other.sign,
740 )
741 } else {
742 let shift = (-exp_diff) as usize;
744 let shifted_self = shift_right_sticky(&self.mantissa, shift);
745 (
746 shifted_self,
747 other.mantissa.clone(),
748 other.exponent,
749 self.sign,
750 other.sign,
751 )
752 }
753 }
754
755 pub fn partial_compare(&self, other: &Self) -> Option<Ordering> {
759 if self.is_nan() || other.is_nan() {
761 return None;
762 }
763
764 if self.is_pos_infinity() {
766 return if other.is_pos_infinity() {
767 Some(Ordering::Equal)
768 } else {
769 Some(Ordering::Greater)
770 };
771 }
772 if self.is_neg_infinity() {
773 return if other.is_neg_infinity() {
774 Some(Ordering::Equal)
775 } else {
776 Some(Ordering::Less)
777 };
778 }
779 if other.is_pos_infinity() {
780 return Some(Ordering::Less);
781 }
782 if other.is_neg_infinity() {
783 return Some(Ordering::Greater);
784 }
785
786 let self_zero = self.is_zero();
788 let other_zero = other.is_zero();
789 if self_zero && other_zero {
790 return Some(Ordering::Equal);
791 }
792 if self_zero {
793 return if other.sign {
794 Some(Ordering::Greater)
795 } else {
796 Some(Ordering::Less)
797 };
798 }
799 if other_zero {
800 return if self.sign {
801 Some(Ordering::Less)
802 } else {
803 Some(Ordering::Greater)
804 };
805 }
806
807 if self.sign != other.sign {
809 return if self.sign {
810 Some(Ordering::Less)
811 } else {
812 Some(Ordering::Greater)
813 };
814 }
815
816 let (m1, m2, _, _, _) = self.align_with(other);
818 let mag_cmp = m1.cmp(&m2);
819
820 if self.sign {
822 Some(mag_cmp.reverse())
823 } else {
824 Some(mag_cmp)
825 }
826 }
827
828 pub fn from_str(s: &str, precision: Precision) -> Option<Self> {
832 let s = s.trim();
833
834 if s.eq_ignore_ascii_case("nan") {
836 return Some(Self::nan(precision));
837 }
838 if s.eq_ignore_ascii_case("inf") || s.eq_ignore_ascii_case("+inf") {
839 return Some(Self::pos_infinity(precision));
840 }
841 if s.eq_ignore_ascii_case("-inf") {
842 return Some(Self::neg_infinity(precision));
843 }
844
845 if let Ok(f) = s.parse::<f64>() {
847 return Some(Self::from_f64(f, precision));
848 }
849
850 None
851 }
852}
853
854impl fmt::Debug for ArbitraryFloat {
855 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
856 if self.is_nan() {
857 write!(f, "NaN")
858 } else if self.is_pos_infinity() {
859 write!(f, "+Inf")
860 } else if self.is_neg_infinity() {
861 write!(f, "-Inf")
862 } else {
863 write!(
864 f,
865 "ArbitraryFloat {{ sign: {}, mantissa: {}, exp: {}, prec: {} }}",
866 self.sign,
867 self.mantissa,
868 self.exponent,
869 self.precision.bits()
870 )
871 }
872 }
873}
874
875impl fmt::Display for ArbitraryFloat {
876 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
877 if self.is_nan() {
878 write!(f, "NaN")
879 } else if self.is_pos_infinity() {
880 write!(f, "+Inf")
881 } else if self.is_neg_infinity() {
882 write!(f, "-Inf")
883 } else {
884 let value = self.to_f64(RoundingMode::RoundNearest);
886 if self.sign && !value.is_sign_negative() {
887 write!(f, "-{}", value.abs())
888 } else {
889 write!(f, "{}", value)
890 }
891 }
892 }
893}
894
895impl PartialEq for ArbitraryFloat {
896 fn eq(&self, other: &Self) -> bool {
897 self.partial_compare(other) == Some(Ordering::Equal)
898 }
899}
900
901impl PartialOrd for ArbitraryFloat {
902 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
903 self.partial_compare(other)
904 }
905}
906
907impl Add for ArbitraryFloat {
908 type Output = Self;
909
910 fn add(self, rhs: Self) -> Self::Output {
911 ArbitraryFloat::add(&self, &rhs, RoundingMode::RoundNearest)
912 }
913}
914
915impl Add for &ArbitraryFloat {
916 type Output = ArbitraryFloat;
917
918 fn add(self, rhs: Self) -> Self::Output {
919 ArbitraryFloat::add(self, rhs, RoundingMode::RoundNearest)
920 }
921}
922
923impl Sub for ArbitraryFloat {
924 type Output = Self;
925
926 fn sub(self, rhs: Self) -> Self::Output {
927 ArbitraryFloat::sub(&self, &rhs, RoundingMode::RoundNearest)
928 }
929}
930
931impl Sub for &ArbitraryFloat {
932 type Output = ArbitraryFloat;
933
934 fn sub(self, rhs: Self) -> Self::Output {
935 ArbitraryFloat::sub(self, rhs, RoundingMode::RoundNearest)
936 }
937}
938
939impl Mul for ArbitraryFloat {
940 type Output = Self;
941
942 fn mul(self, rhs: Self) -> Self::Output {
943 ArbitraryFloat::mul(&self, &rhs, RoundingMode::RoundNearest)
944 }
945}
946
947impl Mul for &ArbitraryFloat {
948 type Output = ArbitraryFloat;
949
950 fn mul(self, rhs: Self) -> Self::Output {
951 ArbitraryFloat::mul(self, rhs, RoundingMode::RoundNearest)
952 }
953}
954
955impl Div for ArbitraryFloat {
956 type Output = Self;
957
958 fn div(self, rhs: Self) -> Self::Output {
959 ArbitraryFloat::div(&self, &rhs, RoundingMode::RoundNearest)
960 }
961}
962
963impl Div for &ArbitraryFloat {
964 type Output = ArbitraryFloat;
965
966 fn div(self, rhs: Self) -> Self::Output {
967 ArbitraryFloat::div(self, rhs, RoundingMode::RoundNearest)
968 }
969}
970
971impl Neg for ArbitraryFloat {
972 type Output = Self;
973
974 fn neg(self) -> Self::Output {
975 ArbitraryFloat::neg(&self)
976 }
977}
978
979impl Neg for &ArbitraryFloat {
980 type Output = ArbitraryFloat;
981
982 fn neg(self) -> Self::Output {
983 ArbitraryFloat::neg(self)
984 }
985}
986
987impl Precision {
989 fn max(self, other: Self) -> Self {
991 if self.bits >= other.bits { self } else { other }
992 }
993}
994
995#[derive(Debug, Clone)]
1000pub struct ArbitraryFloatContext {
1001 pub precision: Precision,
1003 pub rounding: RoundingMode,
1005}
1006
1007impl ArbitraryFloatContext {
1008 pub fn new(precision: Precision, rounding: RoundingMode) -> Self {
1010 Self {
1011 precision,
1012 rounding,
1013 }
1014 }
1015
1016 pub fn zero(&self) -> ArbitraryFloat {
1018 ArbitraryFloat::zero(self.precision)
1019 }
1020
1021 pub fn one(&self) -> ArbitraryFloat {
1023 ArbitraryFloat::one(self.precision)
1024 }
1025
1026 pub fn from_f64(&self, value: f64) -> ArbitraryFloat {
1028 ArbitraryFloat::from_f64(value, self.precision)
1029 }
1030
1031 pub fn add(&self, a: &ArbitraryFloat, b: &ArbitraryFloat) -> ArbitraryFloat {
1033 a.add(b, self.rounding)
1034 }
1035
1036 pub fn sub(&self, a: &ArbitraryFloat, b: &ArbitraryFloat) -> ArbitraryFloat {
1038 a.sub(b, self.rounding)
1039 }
1040
1041 pub fn mul(&self, a: &ArbitraryFloat, b: &ArbitraryFloat) -> ArbitraryFloat {
1043 a.mul(b, self.rounding)
1044 }
1045
1046 pub fn div(&self, a: &ArbitraryFloat, b: &ArbitraryFloat) -> ArbitraryFloat {
1048 a.div(b, self.rounding)
1049 }
1050
1051 pub fn sqrt(&self, a: &ArbitraryFloat) -> ArbitraryFloat {
1053 a.sqrt(self.rounding)
1054 }
1055}
1056
1057impl Default for ArbitraryFloatContext {
1058 fn default() -> Self {
1059 Self::new(Precision::DOUBLE, RoundingMode::RoundNearest)
1060 }
1061}
1062
1063#[cfg(test)]
1064mod tests {
1065 use super::*;
1066
1067 const EPSILON: f64 = 1e-10;
1068
1069 fn approx_eq(a: f64, b: f64) -> bool {
1070 (a - b).abs() < EPSILON || (a.is_nan() && b.is_nan())
1071 }
1072
1073 #[test]
1074 fn test_precision_constants() {
1075 assert_eq!(Precision::DOUBLE.bits(), 53);
1076 assert_eq!(Precision::EXTENDED.bits(), 64);
1077 assert_eq!(Precision::QUAD.bits(), 113);
1078 assert_eq!(Precision::HIGH.bits(), 256);
1079 }
1080
1081 #[test]
1082 fn test_from_f64_basic() {
1083 let prec = Precision::new(64);
1084 let f = ArbitraryFloat::from_f64(core::f64::consts::PI, prec);
1085 let back = f.to_f64(RoundingMode::RoundNearest);
1086 assert!(approx_eq(back, core::f64::consts::PI));
1087 }
1088
1089 #[test]
1090 fn test_from_f64_special_values() {
1091 let prec = Precision::new(64);
1092
1093 let nan = ArbitraryFloat::from_f64(f64::NAN, prec);
1094 assert!(nan.is_nan());
1095
1096 let pos_inf = ArbitraryFloat::from_f64(f64::INFINITY, prec);
1097 assert!(pos_inf.is_pos_infinity());
1098
1099 let neg_inf = ArbitraryFloat::from_f64(f64::NEG_INFINITY, prec);
1100 assert!(neg_inf.is_neg_infinity());
1101
1102 let zero = ArbitraryFloat::from_f64(0.0, prec);
1103 assert!(zero.is_zero());
1104 }
1105
1106 #[test]
1107 fn test_addition() {
1108 let prec = Precision::new(64);
1109 let a = ArbitraryFloat::from_f64(1.5, prec);
1110 let b = ArbitraryFloat::from_f64(2.5, prec);
1111 let sum = ArbitraryFloat::add(&a, &b, RoundingMode::RoundNearest);
1112 assert!(approx_eq(sum.to_f64(RoundingMode::RoundNearest), 4.0));
1113 }
1114
1115 #[test]
1116 fn test_subtraction() {
1117 let prec = Precision::new(64);
1118 let a = ArbitraryFloat::from_f64(5.0, prec);
1119 let b = ArbitraryFloat::from_f64(3.0, prec);
1120 let diff = ArbitraryFloat::sub(&a, &b, RoundingMode::RoundNearest);
1121 assert!(approx_eq(diff.to_f64(RoundingMode::RoundNearest), 2.0));
1122 }
1123
1124 #[test]
1125 fn test_multiplication() {
1126 let prec = Precision::new(64);
1127 let a = ArbitraryFloat::from_f64(3.0, prec);
1128 let b = ArbitraryFloat::from_f64(4.0, prec);
1129 let prod = ArbitraryFloat::mul(&a, &b, RoundingMode::RoundNearest);
1130 assert!(approx_eq(prod.to_f64(RoundingMode::RoundNearest), 12.0));
1131 }
1132
1133 #[test]
1134 fn test_division() {
1135 let prec = Precision::new(64);
1136 let a = ArbitraryFloat::from_f64(10.0, prec);
1137 let b = ArbitraryFloat::from_f64(4.0, prec);
1138 let quot = ArbitraryFloat::div(&a, &b, RoundingMode::RoundNearest);
1139 assert!(approx_eq(quot.to_f64(RoundingMode::RoundNearest), 2.5));
1140 }
1141
1142 #[test]
1143 fn test_sqrt() {
1144 let prec = Precision::new(64);
1145 let a = ArbitraryFloat::from_f64(4.0, prec);
1146 let root = a.sqrt(RoundingMode::RoundNearest);
1147 assert!(approx_eq(root.to_f64(RoundingMode::RoundNearest), 2.0));
1148 }
1149
1150 #[test]
1151 fn test_sqrt_2() {
1152 let prec = Precision::new(128);
1153 let a = ArbitraryFloat::from_f64(2.0, prec);
1154 let root = a.sqrt(RoundingMode::RoundNearest);
1155 let expected = 2.0f64.sqrt();
1156 let result = root.to_f64(RoundingMode::RoundNearest);
1157 assert!(
1158 (result - expected).abs() < 1e-14,
1159 "sqrt(2) = {}, expected {}",
1160 result,
1161 expected
1162 );
1163 }
1164
1165 #[test]
1166 fn test_negation() {
1167 let prec = Precision::new(64);
1168 let a = ArbitraryFloat::from_f64(5.0, prec);
1169 let neg_a = a.neg();
1170 assert!(approx_eq(neg_a.to_f64(RoundingMode::RoundNearest), -5.0));
1171 }
1172
1173 #[test]
1174 fn test_abs() {
1175 let prec = Precision::new(64);
1176 let a = ArbitraryFloat::from_f64(-5.0, prec);
1177 let abs_a = a.abs();
1178 assert!(approx_eq(abs_a.to_f64(RoundingMode::RoundNearest), 5.0));
1179 }
1180
1181 #[test]
1182 fn test_comparison() {
1183 let prec = Precision::new(64);
1184 let a = ArbitraryFloat::from_f64(3.0, prec);
1185 let b = ArbitraryFloat::from_f64(5.0, prec);
1186 let c = ArbitraryFloat::from_f64(3.0, prec);
1187
1188 assert!(a < b);
1189 assert!(b > a);
1190 assert!(a == c);
1191 assert!(a <= c);
1192 assert!(a >= c);
1193 }
1194
1195 #[test]
1196 fn test_comparison_with_nan() {
1197 let prec = Precision::new(64);
1198 let a = ArbitraryFloat::from_f64(3.0, prec);
1199 let nan = ArbitraryFloat::nan(prec);
1200
1201 assert!(a.partial_compare(&nan).is_none());
1202 assert!(nan.partial_compare(&a).is_none());
1203 assert!(nan.partial_compare(&nan).is_none());
1204 }
1205
1206 #[test]
1207 fn test_infinity_operations() {
1208 let prec = Precision::new(64);
1209 let pos_inf = ArbitraryFloat::pos_infinity(prec);
1210 let neg_inf = ArbitraryFloat::neg_infinity(prec);
1211 let one = ArbitraryFloat::from_f64(1.0, prec);
1212
1213 let sum = ArbitraryFloat::add(&pos_inf, &one, RoundingMode::RoundNearest);
1215 assert!(sum.is_pos_infinity());
1216
1217 let sum2 = ArbitraryFloat::add(&pos_inf, &neg_inf, RoundingMode::RoundNearest);
1219 assert!(sum2.is_nan());
1220
1221 let two = ArbitraryFloat::from_f64(2.0, prec);
1223 let prod = ArbitraryFloat::mul(&pos_inf, &two, RoundingMode::RoundNearest);
1224 assert!(prod.is_pos_infinity());
1225
1226 let zero = ArbitraryFloat::zero(prec);
1228 let prod2 = ArbitraryFloat::mul(&pos_inf, &zero, RoundingMode::RoundNearest);
1229 assert!(prod2.is_nan());
1230 }
1231
1232 #[test]
1233 fn test_zero_operations() {
1234 let prec = Precision::new(64);
1235 let zero = ArbitraryFloat::zero(prec);
1236 let one = ArbitraryFloat::from_f64(1.0, prec);
1237
1238 let sum = ArbitraryFloat::add(&zero, &one, RoundingMode::RoundNearest);
1240 assert!(approx_eq(sum.to_f64(RoundingMode::RoundNearest), 1.0));
1241
1242 let prod = ArbitraryFloat::mul(&zero, &one, RoundingMode::RoundNearest);
1244 assert!(prod.is_zero());
1245
1246 let quot = ArbitraryFloat::div(&one, &zero, RoundingMode::RoundNearest);
1248 assert!(quot.is_pos_infinity());
1249
1250 let quot2 = ArbitraryFloat::div(&zero, &zero, RoundingMode::RoundNearest);
1252 assert!(quot2.is_nan());
1253 }
1254
1255 #[test]
1256 fn test_operator_overloads() {
1257 let prec = Precision::new(64);
1258 let a = ArbitraryFloat::from_f64(10.0, prec);
1259 let b = ArbitraryFloat::from_f64(3.0, prec);
1260
1261 let sum = a.clone() + b.clone();
1262 assert!(approx_eq(sum.to_f64(RoundingMode::RoundNearest), 13.0));
1263
1264 let diff = a.clone() - b.clone();
1265 assert!(approx_eq(diff.to_f64(RoundingMode::RoundNearest), 7.0));
1266
1267 let prod = a.clone() * b.clone();
1268 assert!(approx_eq(prod.to_f64(RoundingMode::RoundNearest), 30.0));
1269
1270 let quot = a.clone() / b.clone();
1271 let result = quot.to_f64(RoundingMode::RoundNearest);
1272 assert!(
1273 (result - 10.0 / 3.0).abs() < 1e-10,
1274 "10/3 = {}, expected {}",
1275 result,
1276 10.0 / 3.0
1277 );
1278
1279 let neg = -a;
1280 assert!(approx_eq(neg.to_f64(RoundingMode::RoundNearest), -10.0));
1281 }
1282
1283 #[test]
1284 fn test_context() {
1285 let ctx = ArbitraryFloatContext::new(Precision::new(128), RoundingMode::RoundNearest);
1286
1287 let a = ctx.from_f64(3.5);
1288 let b = ctx.from_f64(2.25);
1289
1290 let sum = ctx.add(&a, &b);
1291 assert!(approx_eq(
1292 sum.to_f64(RoundingMode::RoundNearest),
1293 3.5 + 2.25
1294 ));
1295
1296 let sqrt_2 = ctx.sqrt(&ctx.from_f64(2.0));
1297 assert!((sqrt_2.to_f64(RoundingMode::RoundNearest) - 2.0f64.sqrt()).abs() < 1e-14);
1298 }
1299
1300 #[test]
1301 fn test_from_str() {
1302 let prec = Precision::new(64);
1303
1304 let nan = ArbitraryFloat::from_str("NaN", prec).expect("serialization failed");
1305 assert!(nan.is_nan());
1306
1307 let inf = ArbitraryFloat::from_str("inf", prec).expect("serialization failed");
1308 assert!(inf.is_pos_infinity());
1309
1310 let neg_inf = ArbitraryFloat::from_str("-inf", prec).expect("serialization failed");
1311 assert!(neg_inf.is_neg_infinity());
1312
1313 let val = ArbitraryFloat::from_str("3.5", prec).expect("serialization failed");
1314 assert!(approx_eq(val.to_f64(RoundingMode::RoundNearest), 3.5));
1315 }
1316
1317 #[test]
1318 fn test_high_precision() {
1319 let low_prec = Precision::new(53);
1321 let high_prec = Precision::new(256);
1322
1323 let one_low = ArbitraryFloat::from_f64(1.0, low_prec);
1325 let three_low = ArbitraryFloat::from_f64(3.0, low_prec);
1326 let div_low = ArbitraryFloat::div(&one_low, &three_low, RoundingMode::RoundNearest);
1327 let result_low = ArbitraryFloat::mul(&div_low, &three_low, RoundingMode::RoundNearest);
1328
1329 let one_high = ArbitraryFloat::from_f64(1.0, high_prec);
1330 let three_high = ArbitraryFloat::from_f64(3.0, high_prec);
1331 let div_high = ArbitraryFloat::div(&one_high, &three_high, RoundingMode::RoundNearest);
1332 let result_high = ArbitraryFloat::mul(&div_high, &three_high, RoundingMode::RoundNearest);
1333
1334 let error_low = (result_low.to_f64(RoundingMode::RoundNearest) - 1.0).abs();
1336 let error_high = (result_high.to_f64(RoundingMode::RoundNearest) - 1.0).abs();
1337
1338 assert!(
1339 error_high <= error_low + 1e-15,
1340 "High precision error {} should be <= low precision error {}",
1341 error_high,
1342 error_low
1343 );
1344 }
1345
1346 #[test]
1347 fn test_display() {
1348 let prec = Precision::new(64);
1349
1350 let nan = ArbitraryFloat::nan(prec);
1351 assert_eq!(format!("{}", nan), "NaN");
1352
1353 let pos_inf = ArbitraryFloat::pos_infinity(prec);
1354 assert_eq!(format!("{}", pos_inf), "+Inf");
1355
1356 let neg_inf = ArbitraryFloat::neg_infinity(prec);
1357 assert_eq!(format!("{}", neg_inf), "-Inf");
1358 }
1359}