spl_math/
precise_number.rs

1#![allow(clippy::arithmetic_side_effects)]
2//! Defines PreciseNumber, a U256 wrapper with float-like operations
3
4use crate::uint::U256;
5
6// Allows for easy swapping between different internal representations
7type InnerUint = U256;
8
9/// The representation of the number one as a precise number as 10^12
10pub const ONE: u128 = 1_000_000_000_000;
11
12/// Struct encapsulating a fixed-point number that allows for decimal
13/// calculations
14#[derive(Clone, Debug, PartialEq)]
15pub struct PreciseNumber {
16    /// Wrapper over the inner value, which is multiplied by ONE
17    pub value: InnerUint,
18}
19
20/// The precise-number 1 as a InnerUint
21fn one() -> InnerUint {
22    InnerUint::from(ONE)
23}
24
25/// The number 0 as a PreciseNumber, used for easier calculations.
26fn zero() -> InnerUint {
27    InnerUint::from(0)
28}
29
30impl PreciseNumber {
31    /// Correction to apply to avoid truncation errors on division.  Since
32    /// integer operations will always floor the result, we artificially bump it
33    /// up by one half to get the expect result.
34    fn rounding_correction() -> InnerUint {
35        InnerUint::from(ONE / 2)
36    }
37
38    /// Desired precision for the correction factor applied during each
39    /// iteration of checked_pow_approximation.  Once the correction factor is
40    /// smaller than this number, or we reach the maximum number of iterations,
41    /// the calculation ends.
42    fn precision() -> InnerUint {
43        InnerUint::from(100)
44    }
45
46    fn zero() -> Self {
47        Self { value: zero() }
48    }
49
50    fn one() -> Self {
51        Self { value: one() }
52    }
53
54    /// Maximum number iterations to apply on checked_pow_approximation.
55    const MAX_APPROXIMATION_ITERATIONS: u128 = 100;
56
57    /// Minimum base allowed when calculating exponents in checked_pow_fraction
58    /// and checked_pow_approximation.  This simply avoids 0 as a base.
59    fn min_pow_base() -> InnerUint {
60        InnerUint::from(1)
61    }
62
63    /// Maximum base allowed when calculating exponents in checked_pow_fraction
64    /// and checked_pow_approximation.  The calculation use a Taylor Series
65    /// approximation around 1, which converges for bases between 0 and 2.  See
66    /// https://en.wikipedia.org/wiki/Binomial_series#Conditions_for_convergence
67    /// for more information.
68    fn max_pow_base() -> InnerUint {
69        InnerUint::from(2 * ONE)
70    }
71
72    /// Create a precise number from an imprecise u128, should always succeed
73    pub fn new(value: u128) -> Option<Self> {
74        let value = InnerUint::from(value).checked_mul(one())?;
75        Some(Self { value })
76    }
77
78    /// Convert a precise number back to u128
79    pub fn to_imprecise(&self) -> Option<u128> {
80        self.value
81            .checked_add(Self::rounding_correction())?
82            .checked_div(one())
83            .map(|v| v.as_u128())
84    }
85
86    /// Checks that two PreciseNumbers are equal within some tolerance
87    pub fn almost_eq(&self, rhs: &Self, precision: InnerUint) -> bool {
88        let (difference, _) = self.unsigned_sub(rhs);
89        difference.value < precision
90    }
91
92    /// Checks that a number is less than another
93    pub fn less_than(&self, rhs: &Self) -> bool {
94        self.value < rhs.value
95    }
96
97    /// Checks that a number is greater than another
98    pub fn greater_than(&self, rhs: &Self) -> bool {
99        self.value > rhs.value
100    }
101
102    /// Checks that a number is less than another
103    pub fn less_than_or_equal(&self, rhs: &Self) -> bool {
104        self.value <= rhs.value
105    }
106
107    /// Checks that a number is greater than another
108    pub fn greater_than_or_equal(&self, rhs: &Self) -> bool {
109        self.value >= rhs.value
110    }
111
112    /// Floors a precise value to a precision of ONE
113    pub fn floor(&self) -> Option<Self> {
114        let value = self.value.checked_div(one())?.checked_mul(one())?;
115        Some(Self { value })
116    }
117
118    /// Ceiling a precise value to a precision of ONE
119    pub fn ceiling(&self) -> Option<Self> {
120        let value = self
121            .value
122            .checked_add(one().checked_sub(InnerUint::from(1))?)?
123            .checked_div(one())?
124            .checked_mul(one())?;
125        Some(Self { value })
126    }
127
128    /// Performs a checked division on two precise numbers
129    pub fn checked_div(&self, rhs: &Self) -> Option<Self> {
130        if *rhs == Self::zero() {
131            return None;
132        }
133        match self.value.checked_mul(one()) {
134            Some(v) => {
135                let value = v
136                    .checked_add(Self::rounding_correction())?
137                    .checked_div(rhs.value)?;
138                Some(Self { value })
139            }
140            None => {
141                let value = self
142                    .value
143                    .checked_add(Self::rounding_correction())?
144                    .checked_div(rhs.value)?
145                    .checked_mul(one())?;
146                Some(Self { value })
147            }
148        }
149    }
150
151    /// Performs a multiplication on two precise numbers
152    pub fn checked_mul(&self, rhs: &Self) -> Option<Self> {
153        match self.value.checked_mul(rhs.value) {
154            Some(v) => {
155                let value = v
156                    .checked_add(Self::rounding_correction())?
157                    .checked_div(one())?;
158                Some(Self { value })
159            }
160            None => {
161                let value = if self.value >= rhs.value {
162                    self.value.checked_div(one())?.checked_mul(rhs.value)?
163                } else {
164                    rhs.value.checked_div(one())?.checked_mul(self.value)?
165                };
166                Some(Self { value })
167            }
168        }
169    }
170
171    /// Performs addition of two precise numbers
172    pub fn checked_add(&self, rhs: &Self) -> Option<Self> {
173        let value = self.value.checked_add(rhs.value)?;
174        Some(Self { value })
175    }
176
177    /// Subtracts the argument from self
178    pub fn checked_sub(&self, rhs: &Self) -> Option<Self> {
179        let value = self.value.checked_sub(rhs.value)?;
180        Some(Self { value })
181    }
182
183    /// Performs a subtraction, returning the result and whether the result is
184    /// negative
185    pub fn unsigned_sub(&self, rhs: &Self) -> (Self, bool) {
186        match self.value.checked_sub(rhs.value) {
187            None => {
188                let value = rhs.value.checked_sub(self.value).unwrap();
189                (Self { value }, true)
190            }
191            Some(value) => (Self { value }, false),
192        }
193    }
194
195    /// Performs pow on a precise number
196    pub fn checked_pow(&self, exponent: u128) -> Option<Self> {
197        // For odd powers, start with a multiplication by base since we halve the
198        // exponent at the start
199        let value = if exponent.checked_rem(2)? == 0 {
200            one()
201        } else {
202            self.value
203        };
204        let mut result = Self { value };
205
206        // To minimize the number of operations, we keep squaring the base, and
207        // only push to the result on odd exponents, like a binary decomposition
208        // of the exponent.
209        let mut squared_base = self.clone();
210        let mut current_exponent = exponent.checked_div(2)?;
211        while current_exponent != 0 {
212            squared_base = squared_base.checked_mul(&squared_base)?;
213
214            // For odd exponents, "push" the base onto the value
215            if current_exponent.checked_rem(2)? != 0 {
216                result = result.checked_mul(&squared_base)?;
217            }
218
219            current_exponent = current_exponent.checked_div(2)?;
220        }
221        Some(result)
222    }
223
224    /// Approximate the nth root of a number using a Taylor Series around 1 on
225    /// x ^ n, where 0 < n < 1, result is a precise number.
226    /// Refine the guess for each term, using:
227    ///                                  1                    2
228    /// f(x) = f(a) + f'(a) * (x - a) + --- * f''(a) * (x - a)  + ...
229    ///                                  2!
230    /// For x ^ n, this gives:
231    ///  n    n         n-1           1                  n-2        2
232    /// x  = a  + n * a    (x - a) + --- * n * (n - 1) a     (x - a)  + ...
233    ///                               2!
234    ///
235    /// More simply, this means refining the term at each iteration with:
236    ///
237    /// t_k+1 = t_k * (x - a) * (n + 1 - k) / k
238    ///
239    /// where a = 1, n = power, x = precise_num
240    /// NOTE: this function is private because its accurate range and precision
241    /// have not been estbalished.
242    fn checked_pow_approximation(&self, exponent: &Self, max_iterations: u128) -> Option<Self> {
243        assert!(self.value >= Self::min_pow_base());
244        assert!(self.value <= Self::max_pow_base());
245        let one = Self::one();
246        if *exponent == Self::zero() {
247            return Some(one);
248        }
249        let mut precise_guess = one.clone();
250        let mut term = precise_guess.clone();
251        let (x_minus_a, x_minus_a_negative) = self.unsigned_sub(&precise_guess);
252        let exponent_plus_one = exponent.checked_add(&one)?;
253        let mut negative = false;
254        for k in 1..max_iterations {
255            let k = Self::new(k)?;
256            let (current_exponent, current_exponent_negative) = exponent_plus_one.unsigned_sub(&k);
257            term = term.checked_mul(&current_exponent)?;
258            term = term.checked_mul(&x_minus_a)?;
259            term = term.checked_div(&k)?;
260            if term.value < Self::precision() {
261                break;
262            }
263            if x_minus_a_negative {
264                negative = !negative;
265            }
266            if current_exponent_negative {
267                negative = !negative;
268            }
269            if negative {
270                precise_guess = precise_guess.checked_sub(&term)?;
271            } else {
272                precise_guess = precise_guess.checked_add(&term)?;
273            }
274        }
275        Some(precise_guess)
276    }
277
278    /// Get the power of a number, where the exponent is expressed as a fraction
279    /// (numerator / denominator)
280    /// NOTE: this function is private because its accurate range and precision
281    /// have not been estbalished.
282    #[allow(dead_code)]
283    fn checked_pow_fraction(&self, exponent: &Self) -> Option<Self> {
284        assert!(self.value >= Self::min_pow_base());
285        assert!(self.value <= Self::max_pow_base());
286        let whole_exponent = exponent.floor()?;
287        let precise_whole = self.checked_pow(whole_exponent.to_imprecise()?)?;
288        let (remainder_exponent, negative) = exponent.unsigned_sub(&whole_exponent);
289        assert!(!negative);
290        if remainder_exponent.value == InnerUint::from(0) {
291            return Some(precise_whole);
292        }
293        let precise_remainder = self
294            .checked_pow_approximation(&remainder_exponent, Self::MAX_APPROXIMATION_ITERATIONS)?;
295        precise_whole.checked_mul(&precise_remainder)
296    }
297
298    /// Approximate the nth root of a number using Newton's method
299    /// https://en.wikipedia.org/wiki/Newton%27s_method
300    /// NOTE: this function is private because its accurate range and precision
301    /// have not been established.
302    fn newtonian_root_approximation(
303        &self,
304        root: &Self,
305        mut guess: Self,
306        iterations: u128,
307    ) -> Option<Self> {
308        let zero = Self::zero();
309        if *self == zero {
310            return Some(zero);
311        }
312        if *root == zero {
313            return None;
314        }
315        let one = Self::new(1)?;
316        let root_minus_one = root.checked_sub(&one)?;
317        let root_minus_one_whole = root_minus_one.to_imprecise()?;
318        let mut last_guess = guess.clone();
319        let precision = Self::precision();
320        for _ in 0..iterations {
321            // x_k+1 = ((n - 1) * x_k + A / (x_k ^ (n - 1))) / n
322            let first_term = root_minus_one.checked_mul(&guess)?;
323            let power = guess.checked_pow(root_minus_one_whole);
324            let second_term = match power {
325                Some(num) => self.checked_div(&num)?,
326                None => Self::new(0)?,
327            };
328            guess = first_term.checked_add(&second_term)?.checked_div(root)?;
329            if last_guess.almost_eq(&guess, precision) {
330                break;
331            } else {
332                last_guess = guess.clone();
333            }
334        }
335        Some(guess)
336    }
337
338    /// Based on testing around the limits, this base is the smallest value that
339    /// provides an epsilon 11 digits
340    fn minimum_sqrt_base() -> Self {
341        Self {
342            value: InnerUint::from(0),
343        }
344    }
345
346    /// Based on testing around the limits, this base is the smallest value that
347    /// provides an epsilon of 11 digits
348    fn maximum_sqrt_base() -> Self {
349        Self::new(u128::MAX).unwrap()
350    }
351
352    /// Approximate the square root using Newton's method.  Based on testing,
353    /// this provides a precision of 11 digits for inputs between 0 and
354    /// u128::MAX
355    pub fn sqrt(&self) -> Option<Self> {
356        if self.less_than(&Self::minimum_sqrt_base())
357            || self.greater_than(&Self::maximum_sqrt_base())
358        {
359            return None;
360        }
361        let two = PreciseNumber::new(2)?;
362        let one = PreciseNumber::new(1)?;
363        // A good initial guess is the average of the interval that contains the
364        // input number.  For all numbers, that will be between 1 and the given number.
365        let guess = self.checked_add(&one)?.checked_div(&two)?;
366        self.newtonian_root_approximation(&two, guess, Self::MAX_APPROXIMATION_ITERATIONS)
367    }
368}
369
370#[cfg(test)]
371mod tests {
372    use {super::*, proptest::prelude::*};
373
374    fn check_pow_approximation(base: InnerUint, exponent: InnerUint, expected: InnerUint) {
375        let precision = InnerUint::from(5_000_000); // correct to at least 3 decimal places
376        let base = PreciseNumber { value: base };
377        let exponent = PreciseNumber { value: exponent };
378        let root = base
379            .checked_pow_approximation(&exponent, PreciseNumber::MAX_APPROXIMATION_ITERATIONS)
380            .unwrap();
381        let expected = PreciseNumber { value: expected };
382        assert!(root.almost_eq(&expected, precision));
383    }
384
385    #[test]
386    fn test_root_approximation() {
387        let one = one();
388        // square root
389        check_pow_approximation(one / 4, one / 2, one / 2); // 1/2
390        check_pow_approximation(one * 11 / 10, one / 2, InnerUint::from(1_048808848161u128)); // 1.048808848161
391
392        // 5th root
393        check_pow_approximation(one * 4 / 5, one * 2 / 5, InnerUint::from(914610103850u128));
394        // 0.91461010385
395
396        // 10th root
397        check_pow_approximation(one / 2, one * 4 / 50, InnerUint::from(946057646730u128));
398        // 0.94605764673
399    }
400
401    fn check_pow_fraction(
402        base: InnerUint,
403        exponent: InnerUint,
404        expected: InnerUint,
405        precision: InnerUint,
406    ) {
407        let base = PreciseNumber { value: base };
408        let exponent = PreciseNumber { value: exponent };
409        let power = base.checked_pow_fraction(&exponent).unwrap();
410        let expected = PreciseNumber { value: expected };
411        assert!(power.almost_eq(&expected, precision));
412    }
413
414    #[test]
415    fn test_pow_fraction() {
416        let one = one();
417        let precision = InnerUint::from(50_000_000); // correct to at least 3 decimal places
418        let less_precision = precision * 1_000; // correct to at least 1 decimal place
419        check_pow_fraction(one, one, one, precision);
420        check_pow_fraction(
421            one * 20 / 13,
422            one * 50 / 3,
423            InnerUint::from(1312_534484739100u128),
424            precision,
425        ); // 1312.5344847391
426        check_pow_fraction(one * 2 / 7, one * 49 / 4, InnerUint::from(2163), precision);
427        check_pow_fraction(
428            one * 5000 / 5100,
429            one / 9,
430            InnerUint::from(997802126900u128),
431            precision,
432        ); // 0.99780212695
433           // results get less accurate as the base gets further from 1, so allow
434           // for a greater margin of error
435        check_pow_fraction(
436            one * 2,
437            one * 27 / 5,
438            InnerUint::from(42_224253144700u128),
439            less_precision,
440        ); // 42.2242531447
441        check_pow_fraction(
442            one * 18 / 10,
443            one * 11 / 3,
444            InnerUint::from(8_629769290500u128),
445            less_precision,
446        ); // 8.629769290
447    }
448
449    #[test]
450    fn test_newtonian_approximation() {
451        let test = PreciseNumber::new(0).unwrap();
452        let nth_root = PreciseNumber::new(0).unwrap();
453        let guess = test.checked_div(&nth_root);
454        assert_eq!(guess, Option::None);
455
456        // square root
457        let test = PreciseNumber::new(9).unwrap();
458        let nth_root = PreciseNumber::new(2).unwrap();
459        let guess = test.checked_div(&nth_root).unwrap();
460        let root = test
461            .newtonian_root_approximation(
462                &nth_root,
463                guess,
464                PreciseNumber::MAX_APPROXIMATION_ITERATIONS,
465            )
466            .unwrap()
467            .to_imprecise()
468            .unwrap();
469        assert_eq!(root, 3); // actually 3
470
471        let test = PreciseNumber::new(101).unwrap();
472        let nth_root = PreciseNumber::new(2).unwrap();
473        let guess = test.checked_div(&nth_root).unwrap();
474        let root = test
475            .newtonian_root_approximation(
476                &nth_root,
477                guess,
478                PreciseNumber::MAX_APPROXIMATION_ITERATIONS,
479            )
480            .unwrap()
481            .to_imprecise()
482            .unwrap();
483        assert_eq!(root, 10); // actually 10.049875
484
485        let test = PreciseNumber::new(1_000_000_000).unwrap();
486        let nth_root = PreciseNumber::new(2).unwrap();
487        let guess = test.checked_div(&nth_root).unwrap();
488        let root = test
489            .newtonian_root_approximation(
490                &nth_root,
491                guess,
492                PreciseNumber::MAX_APPROXIMATION_ITERATIONS,
493            )
494            .unwrap()
495            .to_imprecise()
496            .unwrap();
497        assert_eq!(root, 31_623); // actually 31622.7766
498
499        // 5th root
500        let test = PreciseNumber::new(500).unwrap();
501        let nth_root = PreciseNumber::new(5).unwrap();
502        let guess = test.checked_div(&nth_root).unwrap();
503        let root = test
504            .newtonian_root_approximation(
505                &nth_root,
506                guess,
507                PreciseNumber::MAX_APPROXIMATION_ITERATIONS,
508            )
509            .unwrap()
510            .to_imprecise()
511            .unwrap();
512        assert_eq!(root, 3); // actually 3.46572422
513    }
514
515    #[test]
516    fn test_checked_mul() {
517        let number_one = PreciseNumber::new(0).unwrap();
518        let number_two = PreciseNumber::new(0).unwrap();
519        let result = number_one.checked_mul(&number_two);
520        assert_eq!(
521            result,
522            Option::Some(PreciseNumber {
523                value: U256::from(0)
524            })
525        );
526
527        let number_one = PreciseNumber::new(2).unwrap();
528        let number_two = PreciseNumber::new(2).unwrap();
529        let result = number_one.checked_mul(&number_two).unwrap();
530        assert_eq!(result, PreciseNumber::new(2 * 2).unwrap());
531
532        let number_one = PreciseNumber { value: U256::MAX };
533        let number_two = PreciseNumber::new(1).unwrap();
534        let result = number_one.checked_mul(&number_two).unwrap();
535        assert_eq!(result.value, U256::MAX / one() * one());
536
537        let number_one = PreciseNumber { value: U256::MAX };
538        let mut number_two = PreciseNumber::new(1).unwrap();
539        number_two.value += U256::from(1);
540        let result = number_one.checked_mul(&number_two);
541        assert_eq!(result, Option::None);
542    }
543
544    fn check_square_root(check: &PreciseNumber) {
545        let epsilon = PreciseNumber {
546            value: InnerUint::from(10),
547        }; // correct within 11 decimals
548        let one = PreciseNumber::one();
549        let one_plus_epsilon = one.checked_add(&epsilon).unwrap();
550        let one_minus_epsilon = one.checked_sub(&epsilon).unwrap();
551        let approximate_root = check.sqrt().unwrap();
552        let lower_bound = approximate_root
553            .checked_mul(&one_minus_epsilon)
554            .unwrap()
555            .checked_pow(2)
556            .unwrap();
557        let upper_bound = approximate_root
558            .checked_mul(&one_plus_epsilon)
559            .unwrap()
560            .checked_pow(2)
561            .unwrap();
562        assert!(check.less_than_or_equal(&upper_bound));
563        assert!(check.greater_than_or_equal(&lower_bound));
564    }
565
566    #[test]
567    fn test_square_root_min_max() {
568        let test_roots = [
569            PreciseNumber::minimum_sqrt_base(),
570            PreciseNumber::maximum_sqrt_base(),
571        ];
572        for i in test_roots.iter() {
573            check_square_root(i);
574        }
575    }
576
577    #[test]
578    fn test_floor() {
579        let whole_number = PreciseNumber::new(2).unwrap();
580        let mut decimal_number = PreciseNumber::new(2).unwrap();
581        decimal_number.value += InnerUint::from(1);
582        let floor = decimal_number.floor().unwrap();
583        let floor_again = floor.floor().unwrap();
584        assert_eq!(whole_number.value, floor.value);
585        assert_eq!(whole_number.value, floor_again.value);
586    }
587
588    #[test]
589    fn test_ceiling() {
590        let whole_number = PreciseNumber::new(2).unwrap();
591        let mut decimal_number = PreciseNumber::new(2).unwrap();
592        decimal_number.value -= InnerUint::from(1);
593        let ceiling = decimal_number.ceiling().unwrap();
594        let ceiling_again = ceiling.ceiling().unwrap();
595        assert_eq!(whole_number.value, ceiling.value);
596        assert_eq!(whole_number.value, ceiling_again.value);
597    }
598
599    proptest! {
600        #[test]
601        fn test_square_root(a in 0..u128::MAX) {
602            let a = PreciseNumber { value: InnerUint::from(a) };
603            check_square_root(&a);
604        }
605    }
606}