1#[allow(unused_imports)]
9use num_traits::{FromPrimitive, ToPrimitive, Zero};
10
11#[cfg(feature = "std")]
12use std::fmt::{Debug, Display, Formatter, Result};
13
14#[cfg(not(feature = "std"))]
15use core::fmt::{Debug, Display, Formatter, Result};
16
17#[cfg(feature = "std")]
18use std::f64::consts;
19
20#[cfg(not(feature = "std"))]
21use core::f64::consts;
22
23pub trait PrecisionFloat:
28 Clone
29 + Debug
30 + Display
31 + PartialEq
32 + PartialOrd
33 + core::ops::Add<Output = Self>
34 + core::ops::Sub<Output = Self>
35 + core::ops::Mul<Output = Self>
36 + core::ops::Div<Output = Self>
37 + core::ops::Neg<Output = Self>
38 + Send
39 + Sync
40 + 'static
41{
42 fn from_f64(value: f64) -> Self;
44
45 fn to_f64(&self) -> f64;
47
48 fn sqrt_precise(self) -> Self;
50
51 fn powf_precise(self, exp: Self) -> Self;
53
54 fn sin_precise(self) -> Self;
56 fn cos_precise(self) -> Self;
58 fn tan_precise(self) -> Self;
60
61 fn sinh_precise(self) -> Self;
63 fn cosh_precise(self) -> Self;
65 fn tanh_precise(self) -> Self;
67
68 fn ln_precise(self) -> Self;
70
71 fn exp_precise(self) -> Self;
73
74 fn abs_precise(self) -> Self;
76
77 fn epsilon() -> Self;
79
80 fn default_tolerance() -> Self;
82
83 fn orbital_tolerance() -> Self;
85
86 #[allow(non_snake_case)]
88 fn PI() -> Self;
89
90 fn one() -> Self;
92
93 fn zero() -> Self;
95
96 fn sqrt(self) -> Self {
98 self.sqrt_precise()
99 }
100
101 fn abs(self) -> Self {
103 self.abs_precise()
104 }
105
106 fn max(self, other: Self) -> Self;
108
109 fn min(self, other: Self) -> Self;
111
112 fn zero_array_4() -> [Self; 4] {
114 [Self::zero(), Self::zero(), Self::zero(), Self::zero()]
115 }
116
117 fn zero_matrix_4x4() -> [[Self; 4]; 4] {
119 [
120 Self::zero_array_4(),
121 Self::zero_array_4(),
122 Self::zero_array_4(),
123 Self::zero_array_4(),
124 ]
125 }
126
127 fn zero_tensor_4x4x4() -> [[[Self; 4]; 4]; 4] {
129 [
130 Self::zero_matrix_4x4(),
131 Self::zero_matrix_4x4(),
132 Self::zero_matrix_4x4(),
133 Self::zero_matrix_4x4(),
134 ]
135 }
136}
137
138impl PrecisionFloat for f64 {
140 #[inline]
141 fn from_f64(value: f64) -> Self {
142 value
143 }
144
145 #[inline]
146 fn to_f64(&self) -> f64 {
147 *self
148 }
149
150 #[inline]
151 fn sqrt_precise(self) -> Self {
152 self.sqrt()
153 }
154
155 #[inline]
156 fn powf_precise(self, exp: Self) -> Self {
157 self.powf(exp)
158 }
159
160 #[inline]
161 fn sin_precise(self) -> Self {
162 self.sin()
163 }
164
165 #[inline]
166 fn cos_precise(self) -> Self {
167 self.cos()
168 }
169
170 #[inline]
171 fn tan_precise(self) -> Self {
172 self.tan()
173 }
174
175 #[inline]
176 fn sinh_precise(self) -> Self {
177 self.sinh()
178 }
179
180 #[inline]
181 fn cosh_precise(self) -> Self {
182 self.cosh()
183 }
184
185 #[inline]
186 fn tanh_precise(self) -> Self {
187 self.tanh()
188 }
189
190 #[inline]
191 fn ln_precise(self) -> Self {
192 self.ln()
193 }
194
195 #[inline]
196 fn exp_precise(self) -> Self {
197 self.exp()
198 }
199
200 #[inline]
201 fn abs_precise(self) -> Self {
202 self.abs()
203 }
204
205 #[inline]
206 fn epsilon() -> Self {
207 f64::EPSILON
208 }
209
210 #[inline]
211 fn default_tolerance() -> Self {
212 1e-12
213 }
214
215 #[inline]
216 fn orbital_tolerance() -> Self {
217 1e-15
218 }
219
220 #[inline]
221 fn PI() -> Self {
222 consts::PI
223 }
224
225 #[inline]
226 fn one() -> Self {
227 1.0
228 }
229
230 #[inline]
231 fn zero() -> Self {
232 0.0
233 }
234
235 #[inline]
236 fn max(self, other: Self) -> Self {
237 f64::max(self, other)
238 }
239
240 #[inline]
241 fn min(self, other: Self) -> Self {
242 f64::min(self, other)
243 }
244}
245
246#[cfg(feature = "native-precision")]
248#[derive(Clone, Debug)]
249pub struct HighPrecisionFloat {
250 value: rug::Float,
252 precision: u32,
254}
255
256#[cfg(feature = "native-precision")]
257impl HighPrecisionFloat {
258 pub fn with_precision(value: f64, precision: u32) -> Self {
260 Self {
261 value: rug::Float::with_val(precision, value),
262 precision,
263 }
264 }
265
266 pub fn standard(value: f64) -> Self {
268 Self::with_precision(value, 128)
269 }
270
271 pub fn high(value: f64) -> Self {
273 Self::with_precision(value, 256)
274 }
275
276 pub fn precision(&self) -> u32 {
278 self.precision
279 }
280}
281
282#[cfg(feature = "native-precision")]
283impl PartialEq for HighPrecisionFloat {
284 fn eq(&self, other: &Self) -> bool {
285 self.value == other.value
286 }
287}
288
289#[cfg(feature = "native-precision")]
290impl PartialOrd for HighPrecisionFloat {
291 fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
292 self.value.partial_cmp(&other.value)
293 }
294}
295
296#[cfg(feature = "native-precision")]
297impl Display for HighPrecisionFloat {
298 fn fmt(&self, f: &mut Formatter<'_>) -> Result {
299 write!(f, "{}", self.value)
300 }
301}
302
303#[cfg(all(
308 any(feature = "high-precision", feature = "wasm-precision"),
309 not(feature = "native-precision")
310))]
311#[derive(Clone, Debug)]
312pub struct DashuFloat {
313 value: dashu_float::FBig,
315 precision: u32,
317}
318
319#[cfg(all(
320 any(feature = "high-precision", feature = "wasm-precision"),
321 not(feature = "native-precision")
322))]
323impl DashuFloat {
324 pub fn with_precision(value: f64, precision: u32) -> Self {
326 use dashu_float::FBig;
327
328 Self {
329 value: FBig::try_from(value).unwrap_or(FBig::ZERO),
330 precision,
331 }
332 }
333
334 pub fn standard(value: f64) -> Self {
336 Self::with_precision(value, 40)
337 }
338
339 pub fn high(value: f64) -> Self {
341 Self::with_precision(value, 80)
342 }
343
344 pub fn precision(&self) -> u32 {
346 self.precision
347 }
348}
349
350#[cfg(all(
351 any(feature = "high-precision", feature = "wasm-precision"),
352 not(feature = "native-precision")
353))]
354impl PartialEq for DashuFloat {
355 fn eq(&self, other: &Self) -> bool {
356 self.value == other.value
357 }
358}
359
360#[cfg(all(
361 any(feature = "high-precision", feature = "wasm-precision"),
362 not(feature = "native-precision")
363))]
364impl PartialOrd for DashuFloat {
365 fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
366 self.value.partial_cmp(&other.value)
367 }
368}
369
370#[cfg(all(
371 any(feature = "high-precision", feature = "wasm-precision"),
372 not(feature = "native-precision")
373))]
374impl Display for DashuFloat {
375 fn fmt(&self, f: &mut Formatter<'_>) -> Result {
376 write!(f, "{}", self.value)
377 }
378}
379
380#[cfg(all(
381 any(feature = "high-precision", feature = "wasm-precision"),
382 not(feature = "native-precision")
383))]
384impl PrecisionFloat for DashuFloat {
385 #[inline]
386 fn from_f64(value: f64) -> Self {
387 Self::standard(value)
388 }
389
390 #[inline]
391 fn to_f64(&self) -> f64 {
392 f64::try_from(self.value.clone()).unwrap_or(0.0)
394 }
395
396 #[inline]
397 fn sqrt_precise(self) -> Self {
398 let f64_val = f64::try_from(self.value).unwrap_or(0.0);
400 Self::with_precision(f64_val.sqrt(), self.precision)
401 }
402
403 #[inline]
404 fn powf_precise(self, exp: Self) -> Self {
405 let base_f64 = f64::try_from(self.value).unwrap_or(0.0);
406 let exp_f64 = f64::try_from(exp.value).unwrap_or(0.0);
407 Self::with_precision(base_f64.powf(exp_f64), self.precision.min(exp.precision))
408 }
409
410 #[inline]
411 fn sin_precise(self) -> Self {
412 let f64_val = f64::try_from(self.value).unwrap_or(0.0);
413 Self::with_precision(f64_val.sin(), self.precision)
414 }
415
416 #[inline]
417 fn cos_precise(self) -> Self {
418 let f64_val = f64::try_from(self.value).unwrap_or(0.0);
419 Self::with_precision(f64_val.cos(), self.precision)
420 }
421
422 #[inline]
423 fn tan_precise(self) -> Self {
424 let f64_val = f64::try_from(self.value).unwrap_or(0.0);
425 Self::with_precision(f64_val.tan(), self.precision)
426 }
427
428 #[inline]
429 fn sinh_precise(self) -> Self {
430 let f64_val = f64::try_from(self.value).unwrap_or(0.0);
431 Self::with_precision(f64_val.sinh(), self.precision)
432 }
433
434 #[inline]
435 fn cosh_precise(self) -> Self {
436 let f64_val = f64::try_from(self.value).unwrap_or(0.0);
437 Self::with_precision(f64_val.cosh(), self.precision)
438 }
439
440 #[inline]
441 fn tanh_precise(self) -> Self {
442 let f64_val = f64::try_from(self.value).unwrap_or(0.0);
443 Self::with_precision(f64_val.tanh(), self.precision)
444 }
445
446 #[inline]
447 fn ln_precise(self) -> Self {
448 Self {
450 value: self.value.ln(),
451 precision: self.precision,
452 }
453 }
454
455 #[inline]
456 fn exp_precise(self) -> Self {
457 let f64_val = f64::try_from(self.value).unwrap_or(0.0);
458 Self::with_precision(f64_val.exp(), self.precision)
459 }
460
461 #[inline]
462 fn abs_precise(self) -> Self {
463 use dashu_float::ops::Abs;
465 Self {
466 value: self.value.abs(),
467 precision: self.precision,
468 }
469 }
470
471 #[inline]
472 fn epsilon() -> Self {
473 Self::with_precision(1e-40, 40)
475 }
476
477 #[inline]
478 fn default_tolerance() -> Self {
479 Self::with_precision(1e-12, 40)
480 }
481
482 #[inline]
483 fn orbital_tolerance() -> Self {
484 Self::with_precision(1e-15, 80)
485 }
486
487 #[inline]
488 fn PI() -> Self {
489 Self::with_precision(consts::PI, 40)
490 }
491
492 #[inline]
493 fn one() -> Self {
494 Self::with_precision(1.0, 40)
495 }
496
497 #[inline]
498 fn zero() -> Self {
499 Self::with_precision(0.0, 40)
500 }
501
502 #[inline]
503 fn max(self, other: Self) -> Self {
504 let self_f64 = f64::try_from(self.value.clone()).unwrap_or(0.0);
505 let other_f64 = f64::try_from(other.value.clone()).unwrap_or(0.0);
506 if self_f64 >= other_f64 {
507 self
508 } else {
509 other
510 }
511 }
512
513 #[inline]
514 fn min(self, other: Self) -> Self {
515 let self_f64 = f64::try_from(self.value.clone()).unwrap_or(0.0);
516 let other_f64 = f64::try_from(other.value.clone()).unwrap_or(0.0);
517 if self_f64 <= other_f64 {
518 self
519 } else {
520 other
521 }
522 }
523}
524
525#[cfg(all(
527 any(feature = "high-precision", feature = "wasm-precision"),
528 not(feature = "native-precision")
529))]
530impl core::ops::Add for DashuFloat {
531 type Output = Self;
532
533 fn add(self, other: Self) -> Self {
534 Self {
535 value: self.value + other.value,
536 precision: self.precision.min(other.precision),
537 }
538 }
539}
540
541#[cfg(all(
542 any(feature = "high-precision", feature = "wasm-precision"),
543 not(feature = "native-precision")
544))]
545impl core::ops::Sub for DashuFloat {
546 type Output = Self;
547
548 fn sub(self, other: Self) -> Self {
549 Self {
550 value: self.value - other.value,
551 precision: self.precision.min(other.precision),
552 }
553 }
554}
555
556#[cfg(all(
557 any(feature = "high-precision", feature = "wasm-precision"),
558 not(feature = "native-precision")
559))]
560impl core::ops::Mul for DashuFloat {
561 type Output = Self;
562
563 fn mul(self, other: Self) -> Self {
564 Self {
565 value: self.value * other.value,
566 precision: self.precision.min(other.precision),
567 }
568 }
569}
570
571#[cfg(all(
572 any(feature = "high-precision", feature = "wasm-precision"),
573 not(feature = "native-precision")
574))]
575impl core::ops::Div for DashuFloat {
576 type Output = Self;
577
578 fn div(self, other: Self) -> Self {
579 Self {
580 value: self.value / other.value,
581 precision: self.precision.min(other.precision),
582 }
583 }
584}
585
586#[cfg(all(
587 any(feature = "high-precision", feature = "wasm-precision"),
588 not(feature = "native-precision")
589))]
590impl core::ops::Neg for DashuFloat {
591 type Output = Self;
592
593 fn neg(self) -> Self {
594 Self {
595 value: -self.value,
596 precision: self.precision,
597 }
598 }
599}
600
601#[cfg(all(
602 any(feature = "high-precision", feature = "wasm-precision"),
603 not(feature = "native-precision")
604))]
605impl num_traits::Zero for DashuFloat {
606 fn zero() -> Self {
607 Self {
608 value: dashu_float::FBig::ZERO,
609 precision: 40,
610 }
611 }
612
613 fn is_zero(&self) -> bool {
614 f64::try_from(self.value.clone()).unwrap_or(1.0) == 0.0
616 }
617}
618
619#[cfg(all(
620 any(feature = "high-precision", feature = "wasm-precision"),
621 not(feature = "native-precision")
622))]
623impl num_traits::One for DashuFloat {
624 fn one() -> Self {
625 Self {
626 value: dashu_float::FBig::ONE,
627 precision: 40,
628 }
629 }
630}
631
632#[cfg(all(
633 any(feature = "high-precision", feature = "wasm-precision"),
634 not(feature = "native-precision")
635))]
636impl num_traits::FromPrimitive for DashuFloat {
637 fn from_f64(n: f64) -> Option<Self> {
638 Some(<DashuFloat as PrecisionFloat>::from_f64(n))
639 }
640
641 fn from_f32(n: f32) -> Option<Self> {
642 Some(<DashuFloat as PrecisionFloat>::from_f64(n as f64))
643 }
644
645 fn from_i64(n: i64) -> Option<Self> {
646 Some(<DashuFloat as PrecisionFloat>::from_f64(n as f64))
647 }
648
649 fn from_u64(n: u64) -> Option<Self> {
650 Some(<DashuFloat as PrecisionFloat>::from_f64(n as f64))
651 }
652}
653
654pub type StandardFloat = f64;
658
659#[cfg(feature = "native-precision")]
666pub type ExtendedFloat = HighPrecisionFloat;
667
668#[cfg(all(
669 any(feature = "high-precision", feature = "wasm-precision"),
670 not(feature = "native-precision")
671))]
672pub type ExtendedFloat = DashuFloat;
673
674#[cfg(not(any(
675 feature = "native-precision",
676 feature = "wasm-precision",
677 feature = "high-precision"
678)))]
679pub type ExtendedFloat = f64;
680
681#[cfg(test)]
682mod tests {
683 use super::*;
684
685 #[test]
686 fn test_standard_float_precision() {
687 let x = 2.0_f64;
688 assert!((x.sqrt_precise() - f64::sqrt(2.0)).abs() < f64::EPSILON);
689 assert!((x.powf_precise(3.0) - 8.0).abs() < f64::EPSILON);
690 }
691
692 #[test]
693 fn test_default_tolerance() {
694 assert!(f64::default_tolerance() < f64::EPSILON.sqrt());
695 assert!(f64::default_tolerance() > 0.0);
696 }
697
698 #[cfg(feature = "native-precision")]
699 #[test]
700 fn test_high_precision_creation() {
701 let hp = HighPrecisionFloat::standard(consts::PI);
702 assert_eq!(hp.precision(), 128);
703
704 let sp = HighPrecisionFloat::high(consts::E);
705 assert_eq!(sp.precision(), 256);
706 }
707
708 #[cfg(all(
709 any(feature = "high-precision", feature = "wasm-precision"),
710 not(feature = "native-precision")
711 ))]
712 #[test]
713 fn test_dashu_float_creation() {
714 let df = DashuFloat::standard(core::f64::consts::PI);
715 assert_eq!(df.precision(), 40);
716
717 let df_high = DashuFloat::high(core::f64::consts::E);
718 assert_eq!(df_high.precision(), 80);
719 }
720
721 #[cfg(all(
722 any(feature = "high-precision", feature = "wasm-precision"),
723 not(feature = "native-precision")
724 ))]
725 #[test]
726 fn test_dashu_float_precision_operations() {
727 let x = <DashuFloat as PrecisionFloat>::from_f64(2.0);
728 let sqrt_result = x.clone().sqrt_precise();
729 let expected = <DashuFloat as PrecisionFloat>::from_f64(f64::sqrt(2.0));
730
731 let diff = (sqrt_result.to_f64() - expected.to_f64()).abs();
733 assert!(diff < 1e-10, "sqrt precision test failed: diff = {}", diff);
734
735 let power_result = x
737 .clone()
738 .powf_precise(<DashuFloat as PrecisionFloat>::from_f64(3.0));
739 let power_expected = <DashuFloat as PrecisionFloat>::from_f64(8.0);
740 let power_diff = (power_result.to_f64() - power_expected.to_f64()).abs();
741 assert!(
742 power_diff < 1e-10,
743 "power precision test failed: diff = {}",
744 power_diff
745 );
746 }
747
748 #[cfg(all(
749 any(feature = "high-precision", feature = "wasm-precision"),
750 not(feature = "native-precision")
751 ))]
752 #[test]
753 fn test_dashu_float_arithmetic() {
754 let a = <DashuFloat as PrecisionFloat>::from_f64(3.0);
755 let b = <DashuFloat as PrecisionFloat>::from_f64(4.0);
756
757 let sum = a.clone() + b.clone();
758 assert!((sum.to_f64() - 7.0).abs() < 1e-10);
759
760 let product = a.clone() * b.clone();
761 assert!((product.clone().to_f64() - 12.0).abs() < 1e-10);
762
763 let quotient = product / a;
764 assert!((quotient.to_f64() - 4.0).abs() < 1e-10);
765 }
766
767 #[cfg(all(
768 any(feature = "high-precision", feature = "wasm-precision"),
769 not(feature = "native-precision")
770 ))]
771 #[test]
772 fn test_dashu_float_transcendental() {
773 let x = <DashuFloat as PrecisionFloat>::from_f64(1.0);
774
775 let pi_half = <DashuFloat as PrecisionFloat>::from_f64(core::f64::consts::PI / 2.0);
777 let sin_result = pi_half.clone().sin_precise();
778 let cos_result = pi_half.cos_precise();
779
780 assert!((sin_result.to_f64() - 1.0).abs() < 1e-6);
782 assert!(cos_result.to_f64().abs() < 1e-6);
784
785 let exp_result = x.clone().exp_precise();
787 let ln_exp_result = exp_result.ln_precise();
788 assert!((ln_exp_result.to_f64() - 1.0).abs() < 1e-10);
789 }
790}