1use crate::traits::Abs;
2use fixnum::ops::{Bounded, CheckedAdd, One, RoundMode, RoundingMul, Zero};
3use fixnum::{FixedPoint, Precision};
4use thiserror::Error;
5
6#[derive(Error, Debug, Ord, PartialOrd, Eq, PartialEq)]
7pub enum ApproxEqError<F> {
8 #[error("Expected absolute tolerance to be non-negative, got {0:?}")]
9 NegativeAbsoluteTolerance(F),
10 #[error("Expected percentage to be in interval [0, 1), got {0:?}")]
11 IncorrectRelativePercentage(F),
12}
13
14#[inline]
15fn are_approx_eq_abs_unchecked<F>(left: F, right: F, tolerance: F) -> bool
16where
17 F: Ord + Zero + CheckedAdd<Output = F> + Bounded + Clone,
18{
19 left.clone() <= right.clone().saturating_add(tolerance.clone())
20 && right <= left.saturating_add(tolerance)
21}
22
23pub fn are_approx_eq_abs<F>(left: F, right: F, tolerance: F) -> Result<bool, ApproxEqError<F>>
26where
27 F: Ord + Zero + CheckedAdd<Output = F> + Bounded + Clone,
28{
29 if tolerance >= F::ZERO {
30 Ok(are_approx_eq_abs_unchecked(left, right, tolerance))
31 } else {
32 Err(ApproxEqError::NegativeAbsoluteTolerance(tolerance))
33 }
34}
35
36fn calculate_relative_tolerance<I, P>(
39 a: FixedPoint<I, P>,
40 b: FixedPoint<I, P>,
41 percentage: FixedPoint<I, P>,
42) -> Result<FixedPoint<I, P>, ApproxEqError<FixedPoint<I, P>>>
43where
44 I: Ord + From<i16>,
45 P: Precision + Ord,
46 FixedPoint<I, P>: Zero
47 + One
48 + Abs
49 + Bounded
50 + CheckedAdd<Output = FixedPoint<I, P>>
51 + RoundingMul<Output = FixedPoint<I, P>>,
52{
53 let percentage_correct =
54 percentage >= FixedPoint::<I, P>::ZERO && percentage < FixedPoint::<I, P>::ONE;
55 if !percentage_correct {
56 return Err(ApproxEqError::IncorrectRelativePercentage(percentage));
57 }
58
59 let magnitude = a
60 .trait_abs()
61 .unwrap_or(FixedPoint::<I, P>::MAX)
62 .saturating_add(b.trait_abs().unwrap_or(FixedPoint::<I, P>::MAX));
63 Ok(magnitude.saturating_rmul(percentage, RoundMode::Ceil))
65}
66
67pub fn are_approx_eq_rel<I, P>(
70 left: FixedPoint<I, P>,
71 right: FixedPoint<I, P>,
72 percentage: FixedPoint<I, P>,
73) -> Result<bool, ApproxEqError<FixedPoint<I, P>>>
74where
75 I: Ord + From<i16>,
76 P: Precision + Ord,
77 FixedPoint<I, P>: Zero
78 + One
79 + Abs
80 + Bounded
81 + Clone
82 + CheckedAdd<Output = FixedPoint<I, P>>
83 + RoundingMul<Output = FixedPoint<I, P>>,
84{
85 let tolerance = calculate_relative_tolerance(left.clone(), right.clone(), percentage)?;
86 are_approx_eq_abs(left, right, tolerance)
87}
88
89pub fn are_approx_eq<I, P>(
103 left: FixedPoint<I, P>,
104 right: FixedPoint<I, P>,
105 absolute_tolerance: FixedPoint<I, P>,
106 relative_percentage: FixedPoint<I, P>,
107) -> Result<bool, ApproxEqError<FixedPoint<I, P>>>
108where
109 I: Ord + From<i16>,
110 P: Precision + Ord,
111 FixedPoint<I, P>: Zero
112 + One
113 + Abs
114 + Bounded
115 + Clone
116 + CheckedAdd<Output = FixedPoint<I, P>>
117 + RoundingMul<Output = FixedPoint<I, P>>,
118{
119 let relative_tolerance =
120 calculate_relative_tolerance(left.clone(), right.clone(), relative_percentage)?;
121 if absolute_tolerance >= FixedPoint::<I, P>::ZERO {
123 Ok(are_approx_eq_abs_unchecked(
124 left,
125 right,
126 absolute_tolerance.max(relative_tolerance),
127 ))
128 } else {
129 Err(ApproxEqError::NegativeAbsoluteTolerance(absolute_tolerance))
130 }
131}
132
133#[cfg(test)]
134mod test {
135 use super::{are_approx_eq, are_approx_eq_abs, are_approx_eq_rel, ApproxEqError};
136 use fixnum::ops::{Bounded, CheckedSub, One, Zero};
137 use fixnum::typenum::U18;
138 use fixnum::{fixnum_const, FixedPoint};
139
140 type CustomPrecision = U18;
141
142 #[test]
143 fn should_approx_eq_equalize_exact_numbers() {
144 for number in [
145 FixedPoint::<i128, CustomPrecision>::ZERO,
146 FixedPoint::<i128, CustomPrecision>::MAX,
147 FixedPoint::<i128, CustomPrecision>::MIN,
148 FixedPoint::<i128, CustomPrecision>::from_bits(1),
149 FixedPoint::<i128, CustomPrecision>::from_bits(-1),
150 ] {
151 assert!(are_approx_eq(
152 number,
153 number,
154 FixedPoint::<i128, CustomPrecision>::ZERO,
155 FixedPoint::<i128, CustomPrecision>::ZERO
156 )
157 .unwrap());
158 assert!(are_approx_eq(
160 number,
161 number,
162 FixedPoint::<i128, CustomPrecision>::from_bits(1),
163 FixedPoint::<i128, CustomPrecision>::ZERO
164 )
165 .unwrap());
166 assert!(are_approx_eq(
167 number,
168 number,
169 FixedPoint::<i128, CustomPrecision>::ZERO,
170 FixedPoint::<i128, CustomPrecision>::from_bits(1)
171 )
172 .unwrap());
173 assert!(are_approx_eq(
174 number,
175 number,
176 FixedPoint::<i128, CustomPrecision>::from_bits(1),
177 FixedPoint::<i128, CustomPrecision>::from_bits(1)
178 )
179 .unwrap());
180 assert!(are_approx_eq(
182 number,
183 number,
184 FixedPoint::<i128, CustomPrecision>::MAX,
185 FixedPoint::<i128, CustomPrecision>::ZERO
186 )
187 .unwrap());
188 assert!(are_approx_eq(
189 number,
190 number,
191 FixedPoint::<i128, CustomPrecision>::ZERO,
192 FixedPoint::<i128, CustomPrecision>::ONE
193 .csub(FixedPoint::<i128, CustomPrecision>::from_bits(1))
194 .unwrap()
195 )
196 .unwrap());
197 assert!(are_approx_eq(
198 number,
199 number,
200 FixedPoint::<i128, CustomPrecision>::MAX,
201 FixedPoint::<i128, CustomPrecision>::ONE
202 .csub(FixedPoint::from_bits(1))
203 .unwrap()
204 )
205 .unwrap());
206 }
207 }
208
209 #[test]
210 fn should_approx_eq_abs_equalize_exact_numbers() {
211 for number in [
212 FixedPoint::<i128, CustomPrecision>::ZERO,
213 FixedPoint::<i128, CustomPrecision>::MAX,
214 FixedPoint::<i128, CustomPrecision>::MIN,
215 FixedPoint::<i128, CustomPrecision>::from_bits(1),
216 FixedPoint::<i128, CustomPrecision>::from_bits(-1),
217 ] {
218 assert!(
219 are_approx_eq_abs(number, number, FixedPoint::<i128, CustomPrecision>::ZERO)
220 .unwrap()
221 );
222 assert!(are_approx_eq_abs(
223 number,
224 number,
225 FixedPoint::<i128, CustomPrecision>::from_bits(1)
226 )
227 .unwrap());
228 assert!(
229 are_approx_eq_abs(number, number, FixedPoint::<i128, CustomPrecision>::MAX)
230 .unwrap()
231 );
232 }
233 }
234
235 #[test]
236 fn should_approx_eq_rel_equalize_exact_numbers() {
237 for number in [
238 FixedPoint::<i128, CustomPrecision>::ZERO,
239 FixedPoint::<i128, CustomPrecision>::MAX,
240 FixedPoint::<i128, CustomPrecision>::MIN,
241 FixedPoint::<i128, CustomPrecision>::from_bits(1),
242 FixedPoint::<i128, CustomPrecision>::from_bits(-1),
243 ] {
244 assert!(
245 are_approx_eq_rel(number, number, FixedPoint::<i128, CustomPrecision>::ZERO)
246 .unwrap()
247 );
248 assert!(are_approx_eq_rel(
249 number,
250 number,
251 FixedPoint::<i128, CustomPrecision>::from_bits(1)
252 )
253 .unwrap());
254 assert!(are_approx_eq_rel(
255 number,
256 number,
257 FixedPoint::<i128, CustomPrecision>::ONE
258 .csub(FixedPoint::from_bits(1))
259 .unwrap()
260 )
261 .unwrap());
262 }
263 }
264
265 struct ApproxEqTestCase {
268 left: FixedPoint<i128, CustomPrecision>,
269 right: FixedPoint<i128, CustomPrecision>,
270 absolute_tolerance: FixedPoint<i128, CustomPrecision>,
271 relative_percentage: FixedPoint<i128, CustomPrecision>,
272 }
273
274 impl ApproxEqTestCase {
275 const fn new(
276 left: FixedPoint<i128, CustomPrecision>,
277 right: FixedPoint<i128, CustomPrecision>,
278 absolute_tolerance: FixedPoint<i128, CustomPrecision>,
279 relative_percentage: FixedPoint<i128, CustomPrecision>,
280 ) -> Self {
281 Self {
282 left,
283 right,
284 absolute_tolerance,
285 relative_percentage,
286 }
287 }
288 }
289
290 const APPROX_EQ_ABS_MATCH_CASES: &[ApproxEqTestCase] = &[
292 ApproxEqTestCase::new(
299 fixnum_const!(5, 18),
300 fixnum_const!(0, 18),
301 fixnum_const!(5, 18),
302 fixnum_const!(0.01, 18),
303 ),
304 ApproxEqTestCase::new(
311 fixnum_const!(-5, 18),
312 fixnum_const!(0, 18),
313 fixnum_const!(5, 18),
314 fixnum_const!(0.01, 18),
315 ),
316 ApproxEqTestCase::new(
324 FixedPoint::<i128, CustomPrecision>::from_bits(
325 fixnum::_priv::parse_fixed(stringify!(0.05), fixnum::_priv::pow10(18)) + 1,
326 ),
327 fixnum_const!(0, 18),
328 fixnum_const!(5, 18),
329 fixnum_const!(0.01, 18),
330 ),
331 ApproxEqTestCase::new(
339 FixedPoint::<i128, CustomPrecision>::from_bits(
340 -fixnum::_priv::parse_fixed(stringify!(0.05), fixnum::_priv::pow10(18)) - 1,
341 ),
342 fixnum_const!(0, 18),
343 fixnum_const!(5, 18),
344 fixnum_const!(0.01, 18),
345 ),
346 ApproxEqTestCase::new(
353 fixnum_const!(47, 18),
354 fixnum_const!(52, 18),
355 fixnum_const!(5, 18),
356 fixnum_const!(0.05, 18),
357 ),
358 ApproxEqTestCase::new(
366 fixnum_const!(47.02, 18),
367 fixnum_const!(51.98, 18),
368 fixnum_const!(5, 18),
369 fixnum_const!(0.05, 18),
370 ),
371 ];
372
373 #[test]
374 fn should_approx_eq_match_abs_tolerance() {
375 for &ApproxEqTestCase {
376 left,
377 right,
378 absolute_tolerance,
379 relative_percentage,
380 } in APPROX_EQ_ABS_MATCH_CASES
381 {
382 assert!(
383 are_approx_eq(left, right, absolute_tolerance, relative_percentage).unwrap(),
384 "Expected {} = {} with absolute tolerance {} and relative tolerance (%) {}, but got '!='",
385 left, right, absolute_tolerance, relative_percentage
386 );
387 assert!(
388 are_approx_eq(right, left, absolute_tolerance, relative_percentage).unwrap(),
389 "Expected approx eq to be symmetrical; {} = {}, but {} != {} for abs tolerance {} rel tolerance (%) {}",
390 left, right, right, left, absolute_tolerance, relative_percentage
391 );
392 }
393 }
394
395 #[test]
396 fn should_approx_eq_abs_match_abs_tolerance() {
397 for &ApproxEqTestCase {
398 left,
399 right,
400 absolute_tolerance,
401 relative_percentage: _,
402 } in APPROX_EQ_ABS_MATCH_CASES
403 {
404 assert!(
405 are_approx_eq_abs(left, right, absolute_tolerance).unwrap(),
406 "Expected {} = {} with absolute tolerance {}, but got '!='",
407 left,
408 right,
409 absolute_tolerance
410 );
411 assert!(
412 are_approx_eq_abs(right, left, absolute_tolerance).unwrap(),
413 "Expected approx eq to be symmetrical; {} = {}, but {} != {} for abs tolerance {}",
414 left,
415 right,
416 right,
417 left,
418 absolute_tolerance
419 );
420 }
421 }
422
423 #[test]
424 fn should_approx_eq_rel_not_match_abs_tolerance() {
425 for &ApproxEqTestCase {
426 left,
427 right,
428 absolute_tolerance: _,
429 relative_percentage,
430 } in APPROX_EQ_ABS_MATCH_CASES
431 {
432 assert!(
433 !are_approx_eq_rel(left, right, relative_percentage).unwrap(),
434 "Expected {} != {} with relative tolerance (%) {}, but got '='",
435 left,
436 right,
437 relative_percentage
438 );
439 assert!(
440 !are_approx_eq_rel(right, left, relative_percentage).unwrap(),
441 "Expected approx eq to be symmetrical; {} != {}, but {} = {} for rel tolerance (%) {}",
442 left, right, right, left, relative_percentage
443 );
444 }
445 }
446 const APPROX_EQ_REL_MATCH_CASES: &[ApproxEqTestCase] = &[
448 ApproxEqTestCase::new(
456 fixnum_const!(6, 18),
457 fixnum_const!(5, 18),
458 fixnum_const!(0, 18),
459 fixnum_const!(0.1, 18),
460 ),
461 ApproxEqTestCase::new(
469 fixnum_const!(11, 18),
470 fixnum_const!(9, 18),
471 fixnum_const!(0, 18),
472 fixnum_const!(0.1, 18),
473 ),
474 ApproxEqTestCase::new(
482 fixnum_const!(11, 18),
483 fixnum_const!(9, 18),
484 fixnum_const!(1.9999, 18),
485 fixnum_const!(0.1, 18),
486 ),
487 ApproxEqTestCase::new(
495 fixnum_const!(9, 18),
496 fixnum_const!(10.1, 18),
497 fixnum_const!(1, 18),
498 fixnum_const!(0.1, 18),
499 ),
500 ];
501
502 #[test]
503 fn should_approx_eq_match_rel_tolerance() {
504 for &ApproxEqTestCase {
505 left,
506 right,
507 absolute_tolerance,
508 relative_percentage,
509 } in APPROX_EQ_REL_MATCH_CASES
510 {
511 assert!(
512 are_approx_eq(left, right, absolute_tolerance, relative_percentage).unwrap(),
513 "Expected {} = {} with absolute tolerance {} and relative tolerance (%) {}, but got '!='",
514 left, right, absolute_tolerance, relative_percentage
515 );
516 assert!(
517 are_approx_eq(right, left, absolute_tolerance, relative_percentage).unwrap(),
518 "Expected approx eq to be symmetrical; {} = {}, but {} != {} for abs tolerance {} rel tolerance (%) {}",
519 left, right, right, left, absolute_tolerance, relative_percentage
520 );
521 }
522 }
523
524 #[test]
525 fn should_approx_eq_abs_not_match_rel_tolerance() {
526 for &ApproxEqTestCase {
527 left,
528 right,
529 absolute_tolerance,
530 relative_percentage: _,
531 } in APPROX_EQ_REL_MATCH_CASES
532 {
533 assert!(
534 !are_approx_eq_abs(left, right, absolute_tolerance).unwrap(),
535 "Expected {} != {} with absolute tolerance {}, but got '='",
536 left,
537 right,
538 absolute_tolerance
539 );
540 assert!(
541 !are_approx_eq_abs(right, left, absolute_tolerance).unwrap(),
542 "Expected approx eq to be symmetrical; {} != {}, but {} = {} for abs tolerance {}",
543 left,
544 right,
545 right,
546 left,
547 absolute_tolerance
548 );
549 }
550 }
551
552 #[test]
553 fn should_approx_eq_rel_match_rel_tolerance() {
554 for &ApproxEqTestCase {
555 left,
556 right,
557 absolute_tolerance: _,
558 relative_percentage,
559 } in APPROX_EQ_REL_MATCH_CASES
560 {
561 assert!(
562 are_approx_eq_rel(left, right, relative_percentage).unwrap(),
563 "Expected {} = {} with relative tolerance (%) {}, but got '!='",
564 left,
565 right,
566 relative_percentage
567 );
568 assert!(
569 are_approx_eq_rel(right, left, relative_percentage).unwrap(),
570 "Expected approx eq to be symmetrical; {} = {}, but {} != {} for rel tolerance (%) {}",
571 left, right, right, left, relative_percentage
572 );
573 }
574 }
575
576 const APPROX_EQ_BOTH_MATCH_CASES: &[ApproxEqTestCase] = &[
578 ApproxEqTestCase::new(
586 fixnum_const!(6, 18),
587 fixnum_const!(5, 18),
588 fixnum_const!(1.1, 18),
589 fixnum_const!(0.1, 18),
590 ),
591 ApproxEqTestCase::new(
599 fixnum_const!(9, 18),
600 fixnum_const!(11, 18),
601 fixnum_const!(2, 18),
602 fixnum_const!(0.1, 18),
603 ),
604 ApproxEqTestCase::new(
612 fixnum_const!(9, 18),
613 FixedPoint::<i128, CustomPrecision>::from_bits(
614 fixnum::_priv::parse_fixed(stringify!(9), fixnum::_priv::pow10(18)) + 1,
615 ),
616 fixnum_const!(2, 18),
617 fixnum_const!(0.1, 18),
618 ),
619 ApproxEqTestCase::new(
627 fixnum_const!(9, 18),
628 fixnum_const!(10.1, 18),
629 fixnum_const!(1.11, 18),
630 fixnum_const!(0.1, 18),
631 ),
632 ];
633
634 #[test]
635 fn should_approx_eq_match_both_tolerance() {
636 for &ApproxEqTestCase {
637 left,
638 right,
639 absolute_tolerance,
640 relative_percentage,
641 } in APPROX_EQ_BOTH_MATCH_CASES
642 {
643 assert!(
644 are_approx_eq(left, right, absolute_tolerance, relative_percentage).unwrap(),
645 "Expected {} = {} with absolute tolerance {} and relative tolerance (%) {}, but got '!='",
646 left, right, absolute_tolerance, relative_percentage
647 );
648 assert!(
649 are_approx_eq(right, left, absolute_tolerance, relative_percentage).unwrap(),
650 "Expected approx eq to be symmetrical; {} = {}, but {} != {} for abs tolerance {} rel tolerance (%) {}",
651 left, right, right, left, absolute_tolerance, relative_percentage
652 );
653 }
654 }
655
656 #[test]
657 fn should_approx_eq_abs_match_both_tolerance() {
658 for &ApproxEqTestCase {
659 left,
660 right,
661 absolute_tolerance,
662 relative_percentage: _,
663 } in APPROX_EQ_BOTH_MATCH_CASES
664 {
665 assert!(
666 are_approx_eq_abs(left, right, absolute_tolerance).unwrap(),
667 "Expected {} = {} with absolute tolerance {}, but got '!='",
668 left,
669 right,
670 absolute_tolerance
671 );
672 assert!(
673 are_approx_eq_abs(right, left, absolute_tolerance).unwrap(),
674 "Expected approx eq to be symmetrical; {} = {}, but {} != {} for abs tolerance {}",
675 left,
676 right,
677 right,
678 left,
679 absolute_tolerance
680 );
681 }
682 }
683
684 #[test]
685 fn should_approx_eq_rel_match_both_tolerance() {
686 for &ApproxEqTestCase {
687 left,
688 right,
689 absolute_tolerance: _,
690 relative_percentage,
691 } in APPROX_EQ_BOTH_MATCH_CASES
692 {
693 assert!(
694 are_approx_eq_rel(left, right, relative_percentage).unwrap(),
695 "Expected {} = {} with relative tolerance (%) {}, but got '!='",
696 left,
697 right,
698 relative_percentage
699 );
700 assert!(
701 are_approx_eq_rel(right, left, relative_percentage).unwrap(),
702 "Expected approx eq to be symmetrical; {} = {}, but {} != {} for rel tolerance (%) {}",
703 left, right, right, left, relative_percentage
704 );
705 }
706 }
707
708 const APPROX_EQ_NOT_MATCH_CASES: &[ApproxEqTestCase] = &[
710 ApproxEqTestCase::new(
717 FixedPoint::<i128, CustomPrecision>::from_bits(
718 fixnum::_priv::parse_fixed(stringify!(5), fixnum::_priv::pow10(18)) + 1,
719 ),
720 fixnum_const!(0, 18),
721 fixnum_const!(5, 18),
722 fixnum_const!(0.01, 18),
723 ),
724 ApproxEqTestCase::new(
731 FixedPoint::<i128, CustomPrecision>::from_bits(
732 -fixnum::_priv::parse_fixed(stringify!(5), fixnum::_priv::pow10(18)) - 1,
733 ),
734 fixnum_const!(0, 18),
735 fixnum_const!(5, 18),
736 fixnum_const!(0.01, 18),
737 ),
738 ApproxEqTestCase::new(
745 FixedPoint::<i128, CustomPrecision>::MAX,
746 fixnum_const!(0, 18),
747 fixnum_const!(5, 18),
748 fixnum_const!(0.01, 18),
749 ),
750 ApproxEqTestCase::new(
757 FixedPoint::<i128, CustomPrecision>::MIN,
758 fixnum_const!(0, 18),
759 fixnum_const!(5, 18),
760 fixnum_const!(0.01, 18),
761 ),
762 ApproxEqTestCase::new(
769 FixedPoint::<i128, CustomPrecision>::from_bits(fixnum::_priv::parse_fixed(
770 stringify!(47),
771 fixnum::_priv::pow10(18) - 1,
772 )),
773 FixedPoint::<i128, CustomPrecision>::from_bits(fixnum::_priv::parse_fixed(
774 stringify!(52),
775 fixnum::_priv::pow10(18) + 1,
776 )),
777 fixnum_const!(5, 18),
778 fixnum_const!(0.05, 18),
779 ),
780 ApproxEqTestCase::new(
787 FixedPoint::<i128, CustomPrecision>::from_bits(fixnum::_priv::parse_fixed(
788 stringify!(47),
789 fixnum::_priv::pow10(18) - 1,
790 )),
791 FixedPoint::<i128, CustomPrecision>::from_bits(fixnum::_priv::parse_fixed(
792 stringify!(53),
793 fixnum::_priv::pow10(18) + 1,
794 )),
795 fixnum_const!(5, 18),
796 fixnum_const!(0.05, 18),
797 ),
798 ApproxEqTestCase::new(
806 fixnum_const!(9, 18),
807 FixedPoint::<i128, CustomPrecision>::from_bits(fixnum::_priv::parse_fixed(
808 stringify!(11),
809 fixnum::_priv::pow10(18) + 10,
810 )),
811 fixnum_const!(0, 18),
812 fixnum_const!(0.1, 18),
813 ),
814 ApproxEqTestCase::new(
822 fixnum_const!(9, 18),
823 FixedPoint::<i128, CustomPrecision>::from_bits(fixnum::_priv::parse_fixed(
824 stringify!(11),
825 fixnum::_priv::pow10(18) + 10,
826 )),
827 fixnum_const!(1.9999, 18),
828 fixnum_const!(0.1, 18),
829 ),
830 ];
831
832 #[test]
833 fn should_approx_eq_not_match() {
834 for &ApproxEqTestCase {
835 left,
836 right,
837 absolute_tolerance,
838 relative_percentage,
839 } in APPROX_EQ_NOT_MATCH_CASES
840 {
841 assert!(
842 !are_approx_eq(left, right, absolute_tolerance, relative_percentage).unwrap(),
843 "Expected {} != {} with absolute tolerance {} and relative tolerance (%) {}, but got '=='",
844 left, right, absolute_tolerance, relative_percentage
845 );
846 assert!(
847 !are_approx_eq(right, left, absolute_tolerance, relative_percentage).unwrap(),
848 "Expected approx eq to be symmetrical; {} != {}, but {} = {} for abs tolerance {} rel tolerance (%) {}",
849 left, right, right, left, absolute_tolerance, relative_percentage
850 );
851 }
852 }
853
854 #[test]
855 fn should_fail_incorrect_relative_percentage() {
856 let percentage = FixedPoint::<i128, CustomPrecision>::from_bits(-1234);
857 assert_eq!(
858 are_approx_eq(
859 FixedPoint::<i128, CustomPrecision>::ZERO,
860 FixedPoint::<i128, CustomPrecision>::ZERO,
861 FixedPoint::<i128, CustomPrecision>::ZERO,
862 percentage,
863 ),
864 Err(ApproxEqError::IncorrectRelativePercentage(percentage))
865 );
866 let percentage = FixedPoint::<i128, CustomPrecision>::ONE;
867 assert_eq!(
868 are_approx_eq(
869 FixedPoint::<i128, CustomPrecision>::ZERO,
870 FixedPoint::<i128, CustomPrecision>::ZERO,
871 FixedPoint::<i128, CustomPrecision>::ZERO,
872 percentage,
873 ),
874 Err(ApproxEqError::IncorrectRelativePercentage(percentage))
875 );
876 }
877
878 #[test]
879 fn should_fail_incorrect_absolute_percentage() {
880 let abs_tolerance = FixedPoint::<i128, CustomPrecision>::from_bits(-1);
881 assert_eq!(
882 are_approx_eq(
883 FixedPoint::<i128, CustomPrecision>::ZERO,
884 FixedPoint::<i128, CustomPrecision>::ZERO,
885 abs_tolerance,
886 FixedPoint::<i128, CustomPrecision>::ZERO,
887 ),
888 Err(ApproxEqError::NegativeAbsoluteTolerance(abs_tolerance))
889 );
890 let abs_tolerance = FixedPoint::<i128, CustomPrecision>::from_bits(i128::MIN);
891 assert_eq!(
892 are_approx_eq(
893 FixedPoint::<i128, CustomPrecision>::ZERO,
894 FixedPoint::<i128, CustomPrecision>::ZERO,
895 abs_tolerance,
896 FixedPoint::<i128, CustomPrecision>::ZERO,
897 ),
898 Err(ApproxEqError::NegativeAbsoluteTolerance(abs_tolerance))
899 );
900 }
901}