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