num_decimal/
num.rs

1// Copyright (C) 2019-2022 Daniel Mueller <deso@posteo.net>
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4use std::convert::TryFrom;
5use std::error::Error as StdError;
6use std::fmt::Debug;
7use std::fmt::Display;
8use std::fmt::Formatter;
9use std::fmt::Result as FmtResult;
10#[cfg(not(feature = "num-v02"))]
11use std::num::FpCategory;
12use std::ops::Add;
13use std::ops::AddAssign;
14use std::ops::Div;
15use std::ops::DivAssign;
16use std::ops::Mul;
17use std::ops::MulAssign;
18use std::ops::Neg;
19use std::ops::Rem;
20use std::ops::RemAssign;
21use std::ops::Sub;
22use std::ops::SubAssign;
23use std::str::FromStr;
24
25use num_traits::cast::ToPrimitive;
26use num_traits::identities::Zero;
27use num_traits::pow::Pow;
28use num_traits::sign::Signed;
29
30use crate::num_bigint::BigInt;
31use crate::num_bigint::ParseBigIntError;
32use crate::num_bigint::Sign;
33use crate::num_rational::BigRational;
34use crate::num_rational::Rational32;
35use crate::num_rational::Rational64;
36
37
38/// The maximum precision we use when converting a `Num` into a string.
39///
40/// Because we base our implementation on rational numbers which can map
41/// to an infinite sequence in the decimal system we have to put an
42/// upper limit on the maximum precision we can display.
43const MAX_PRECISION: usize = 8;
44
45
46/// Round the given `BigRational` to the nearest integer. Rounding
47/// happens based on the Round-Half-To-Even scheme (also known as the
48/// "banker's rounding" algorithm), which rounds to the closest integer
49/// as expected but if the fractional part is exactly 1/2 (i.e.,
50/// equidistant from two integers) it rounds to the even one of the two.
51fn round_to_even(val: &BigRational) -> BigRational {
52  let zero = BigInt::from(0);
53  let one = BigInt::from(1);
54  let two = BigInt::from(2);
55
56  let zero_ = BigRational::new(zero, one.clone());
57  let half = BigRational::new(one.clone(), two.clone());
58  // Find unsigned fractional part of rational number.
59  let mut fract = val.fract();
60  if fract < zero_ {
61    fract = &zero_ - fract
62  };
63
64  let trunc = val.trunc();
65  if fract == half {
66    // If the denominator is even round down, otherwise round up.
67    if &trunc % two == zero_ {
68      trunc
69    } else if trunc >= zero_ {
70      trunc + one
71    } else {
72      trunc - one
73    }
74  } else {
75    // BigRational::round() behaves as we want it for all cases except
76    // where the fractional part is 1/2.
77    val.round()
78  }
79}
80
81fn format_impl(
82  value: &BigRational,
83  mut result: String,
84  depth: usize,
85  min_precision: usize,
86  precision: Option<usize>,
87) -> String {
88  debug_assert!(min_precision <= precision.unwrap_or(MAX_PRECISION));
89
90  let trunc = value.trunc().to_integer();
91  result += &trunc.to_string();
92
93  let numer = value.numer();
94  let denom = value.denom();
95  let value = numer - (trunc * denom);
96
97  let at_min = depth >= min_precision;
98  let at_max = depth >= precision.unwrap_or(MAX_PRECISION);
99  // If the user specified a precision for the formatting then we
100  // honor that by ensuring that we have that many decimals.
101  // Otherwise we print as many as there are, up to `MAX_PRECISION`.
102  if (value.is_zero() && precision.is_none() && at_min) || at_max {
103    result
104  } else {
105    if depth == 0 {
106      result += ".";
107    }
108
109    let value = BigRational::new(value * 10, denom.clone());
110    format_impl(&value, result, depth + 1, min_precision, precision)
111  }
112}
113
114
115/// An error used for conveying parsing failures.
116#[derive(Debug, PartialEq)]
117pub enum ParseNumError {
118  /// A string could not get parsed as a `Num`.
119  InvalidStrError(String),
120  /// An integer value could not get parsed.
121  ParseIntError(ParseBigIntError),
122}
123
124impl From<ParseBigIntError> for ParseNumError {
125  #[inline]
126  fn from(e: ParseBigIntError) -> Self {
127    Self::ParseIntError(e)
128  }
129}
130
131impl Display for ParseNumError {
132  #[inline]
133  fn fmt(&self, fmt: &mut Formatter<'_>) -> FmtResult {
134    match self {
135      Self::InvalidStrError(s) => write!(fmt, "{}", s),
136      Self::ParseIntError(err) => write!(fmt, "{}", err),
137    }
138  }
139}
140
141impl StdError for ParseNumError {
142  #[inline]
143  fn source(&self) -> Option<&(dyn StdError + 'static)> {
144    match self {
145      Self::InvalidStrError(..) => None,
146      Self::ParseIntError(err) => err.source(),
147    }
148  }
149}
150
151
152/// An object providing more advanced displaying capabilities to `Num`.
153#[derive(Debug)]
154pub struct CustomDisplay<'n> {
155  /// The `Num` object to display.
156  num: &'n Num,
157  /// The minimum precision to use when displaying.
158  min_precision: Option<usize>,
159}
160
161impl<'n> CustomDisplay<'n> {
162  /// Create a new `CustomDisplay` object for displaying the given
163  /// `Num`.
164  #[inline]
165  fn new(num: &'n Num) -> Self {
166    Self {
167      num,
168      min_precision: None,
169    }
170  }
171
172  /// Set the minimum precision used when displaying.
173  ///
174  /// If actual precision is higher, more values will be printed.
175  #[inline]
176  pub fn min_precision(&mut self, min_precision: usize) -> &mut Self {
177    self.min_precision = Some(min_precision);
178    self
179  }
180}
181
182impl<'n> Display for CustomDisplay<'n> {
183  #[inline]
184  fn fmt(&self, fmt: &mut Formatter<'_>) -> FmtResult {
185    self.num.format(fmt, self.min_precision.unwrap_or(0))
186  }
187}
188
189
190macro_rules! impl_num {
191  ($(#[$meta:meta])* pub struct $name:ident($rational:ty), $int:ty) => {
192    $(#[$meta])*
193    pub struct $name(pub(crate) $rational);
194
195    impl $name {
196      /// Construct a rational number from its two components.
197      #[inline]
198      pub fn new<T, U>(numer: T, denom: U) -> Self
199      where
200        $int: From<T>,
201        $int: From<U>,
202      {
203        let numer = <$int>::from(numer);
204        let denom = <$int>::from(denom);
205
206        Self(<$rational>::new(numer, denom))
207      }
208    }
209
210    impl Default for $name {
211      #[inline]
212      fn default() -> Self {
213        <$name>::from(0)
214      }
215    }
216
217    impl From<$name> for ($int, $int) {
218      #[inline]
219      fn from(other: $name) -> Self {
220        other.0.into()
221      }
222    }
223
224    // The default `Debug` implementation is way too verbose. We have no
225    // intention of debugging the underlying Rational type itself. So we
226    // overwrite it here, effectively printing a fraction.
227    impl Debug for $name {
228      #[inline]
229      fn fmt(&self, fmt: &mut Formatter<'_>) -> FmtResult {
230        // We maintain the invariant that the numerator determines the sign
231        // and that the denominator is always positive (as it can never be
232        // zero).
233        debug_assert!(self.0.denom().is_positive());
234
235        write!(fmt, "{}/{}", self.0.numer(), self.0.denom())
236      }
237    }
238  };
239}
240
241
242impl_num! {
243  /// An unlimited precision number type with some improvements and
244  /// customizations over [`BigRational`].
245  #[derive(Clone, Eq, Hash, Ord, PartialEq, PartialOrd)]
246  pub struct Num(BigRational), BigInt
247}
248
249impl Num {
250  /// Round the given `Num` to the nearest integer.
251  ///
252  /// Rounding happens based on the Round-Half-To-Even scheme (also
253  /// known as the "bankers rounding" algorithm), which rounds to the
254  /// closest integer as expected but if the fractional part is exactly
255  /// 1/2 (i.e., equidistant from two integers) it rounds to the even
256  /// one of the two.
257  #[inline]
258  pub fn round(&self) -> Self {
259    self.round_with(0)
260  }
261
262  /// Round the given `Num` with the given precision.
263  ///
264  /// Rounding happens based on the Round-Half-To-Even scheme similar to
265  /// `round`.
266  pub fn round_with(&self, precision: usize) -> Self {
267    let factor = BigInt::from(10).pow(precision);
268    let value = &self.0 * &factor;
269
270    Num(round_to_even(&value).trunc() / factor)
271  }
272
273  /// Round the given `Num` towards zero.
274  #[inline]
275  pub fn trunc(&self) -> Self {
276    Num(self.0.trunc())
277  }
278
279  /// Return the fractional part of the given `Num`, with division rounded towards zero.
280  #[inline]
281  pub fn fract(&self) -> Self {
282    Num(self.0.fract())
283  }
284
285  /// Convert the given `Num` to an integer, rounding towards zero.
286  #[inline]
287  pub fn to_integer(&self) -> BigInt {
288    self.0.to_integer()
289  }
290
291  /// Convert the given `Num` into a `u64`.
292  ///
293  /// The value will be converted into an integer, with rounding towards
294  /// zero. `None` is returned if the resulting integer does not fit
295  /// into 64 bit.
296  #[inline]
297  pub fn to_i64(&self) -> Option<i64> {
298    self.to_integer().to_i64()
299  }
300
301  /// Convert the given `Num` into a `u64`.
302  ///
303  /// The value will be converted into an integer, with rounding towards
304  /// zero. `None` is returned if the resulting integer does not fit
305  /// into 64 bit.
306  #[inline]
307  pub fn to_u64(&self) -> Option<u64> {
308    self.to_integer().to_u64()
309  }
310
311  /// Convert the given `Num` into a `f64`.
312  ///
313  /// `None` is returned if the numerator or the denominator cannot be
314  /// converted into an `f64`.
315  pub fn to_f64(&self) -> Option<f64> {
316    let numer = self.0.numer().to_f64();
317    let denom = self.0.denom().to_f64();
318
319    match (numer, denom) {
320      #[cfg(feature = "num-v02")]
321      (Some(numer), Some(denom)) => Some(numer / denom),
322      #[cfg(not(feature = "num-v02"))]
323      (Some(numer), Some(denom)) => {
324        if !matches!(numer.classify(), FpCategory::Normal | FpCategory::Zero) {
325          return None
326        }
327        if !matches!(denom.classify(), FpCategory::Normal | FpCategory::Zero) {
328          return None
329        }
330        Some(numer / denom)
331      },
332      _ => None,
333    }
334  }
335
336  /// Check if the given `Num` is zero.
337  #[inline]
338  pub fn is_zero(&self) -> bool {
339    self.0.is_zero()
340  }
341
342  /// Check if the given `Num` is positive.
343  #[inline]
344  pub fn is_positive(&self) -> bool {
345    self.0.is_positive()
346  }
347
348  /// Check if the given `Num` is negative.
349  #[inline]
350  pub fn is_negative(&self) -> bool {
351    self.0.is_negative()
352  }
353
354  fn format(&self, fmt: &mut Formatter<'_>, min_precision: usize) -> FmtResult {
355    let non_negative = !self.0.is_negative();
356    let prefix = "";
357
358    let precision = fmt.precision();
359    let value = self.round_with(precision.unwrap_or(MAX_PRECISION)).0.abs();
360    // We want to print out our value (which is a rational) as a
361    // floating point value, for which we need to perform some form of
362    // conversion. We do that using what is pretty much text book long
363    // division.
364    let string = format_impl(&value, String::new(), 0, min_precision, precision);
365    fmt.pad_integral(non_negative, prefix, &string)
366  }
367
368  /// Retrieve a display adapter that can be used for some more
369  /// elaborate formatting needs.
370  #[inline]
371  pub fn display(&self) -> CustomDisplay<'_> {
372    CustomDisplay::new(self)
373  }
374}
375
376impl Display for Num {
377  #[inline]
378  fn fmt(&self, fmt: &mut Formatter<'_>) -> FmtResult {
379    let min_precision = 0;
380    self.format(fmt, min_precision)
381  }
382}
383
384impl From<Num32> for Num {
385  fn from(other: Num32) -> Num {
386    let (numer, denom) = other.into();
387    Num::new(numer, denom)
388  }
389}
390
391impl From<Num64> for Num {
392  fn from(other: Num64) -> Num {
393    let (numer, denom) = other.into();
394    Num::new(numer, denom)
395  }
396}
397
398impl FromStr for Num {
399  type Err = ParseNumError;
400
401  fn from_str(s: &str) -> Result<Self, Self::Err> {
402    fn parse_istr(s: &str) -> Result<(Sign, BigInt), ParseBigIntError> {
403      let val = BigInt::from_str(s)?;
404
405      // BigInt's NoSign is horrible. It represents a value being zero,
406      // but it leads to valid data being discarded silently. Work
407      // around that.
408      let sign = val.sign();
409      let sign = if sign == Sign::NoSign {
410        if s.starts_with('-') {
411          Sign::Minus
412        } else {
413          Sign::Plus
414        }
415      } else {
416        sign
417      };
418      Ok((sign, val))
419    }
420
421    fn parse_str(s: &str, sign: Sign) -> Result<BigInt, ParseNumError> {
422      if s.starts_with('-') || s.starts_with('+') {
423        return Err(ParseNumError::InvalidStrError(s.to_owned()));
424      }
425
426      let num = BigInt::parse_bytes(s.as_bytes(), 10)
427        .ok_or_else(|| ParseNumError::InvalidStrError(s.to_owned()))?;
428      let (_, bytes) = num.to_bytes_le();
429      let num = BigInt::from_bytes_le(sign, &bytes);
430      Ok(num)
431    }
432
433    let mut splits = s.splitn(2, '.');
434    let numer = splits
435      .next()
436      .ok_or_else(|| ParseNumError::InvalidStrError(s.to_owned()))?;
437    let (sign, numer) = parse_istr(numer)?;
438
439    if let Some(s) = splits.next() {
440      let denom = parse_str(s, sign)?;
441      let power = BigInt::from(10).pow(s.len());
442      let numer = numer * &power + denom;
443      Ok(Num(BigRational::new(numer, power)))
444    } else {
445      Ok(Num(BigRational::from_integer(numer)))
446    }
447  }
448}
449
450
451macro_rules! impl_from {
452  ($type:ty) => {
453    impl From<$type> for Num {
454      #[inline]
455      fn from(val: $type) -> Self {
456        Self(BigRational::from_integer(BigInt::from(val)))
457      }
458    }
459  };
460}
461
462impl_from!(i128);
463impl_from!(i16);
464impl_from!(i32);
465impl_from!(i64);
466impl_from!(i8);
467impl_from!(isize);
468impl_from!(u128);
469impl_from!(u16);
470impl_from!(u32);
471impl_from!(u64);
472impl_from!(u8);
473impl_from!(usize);
474impl_from!(BigInt);
475
476
477macro_rules! impl_neg {
478  ($lhs:ty) => {
479    impl Neg for $lhs {
480      type Output = Num;
481
482      #[inline]
483      fn neg(self) -> Self::Output {
484        let Num(int) = self;
485        Num(int.neg())
486      }
487    }
488  };
489}
490
491impl_neg!(Num);
492impl_neg!(&Num);
493
494
495macro_rules! impl_op {
496  (impl $imp:ident, $method:ident, $lhs:ty, $rhs:ty) => {
497    impl $imp<$rhs> for $lhs {
498      type Output = Num;
499
500      #[inline]
501      fn $method(self, rhs: $rhs) -> Self::Output {
502        let Num(lhs) = self;
503        let Num(rhs) = rhs;
504        Num(lhs.$method(rhs))
505      }
506    }
507  };
508}
509
510macro_rules! impl_int_op {
511  (impl $imp:ident, $method:ident, $lhs:ty) => {
512    // Unfortunately we are only able to allow for right hand side
513    // integer types in the operation.
514    impl<T> $imp<T> for $lhs
515    where
516      BigInt: From<T>,
517    {
518      type Output = Num;
519
520      #[inline]
521      fn $method(self, rhs: T) -> Self::Output {
522        let Num(lhs) = self;
523        let rhs = BigRational::from_integer(BigInt::from(rhs));
524        Num(lhs.$method(rhs))
525      }
526    }
527  };
528}
529
530macro_rules! impl_ops {
531  (impl $imp:ident, $method:ident) => {
532    impl_op!(impl $imp, $method, Num, Num);
533    impl_op!(impl $imp, $method, &Num, Num);
534    impl_op!(impl $imp, $method, Num, &Num);
535    impl_op!(impl $imp, $method, &Num, &Num);
536    impl_int_op!(impl $imp, $method, Num);
537    impl_int_op!(impl $imp, $method, &Num);
538  };
539}
540
541impl_ops!(impl Add, add);
542impl_ops!(impl Sub, sub);
543impl_ops!(impl Mul, mul);
544impl_ops!(impl Div, div);
545impl_ops!(impl Rem, rem);
546
547
548macro_rules! impl_assign_op {
549  (impl $imp:ident, $method:ident, $lhs:ty, $rhs:ty) => {
550    impl $imp<$rhs> for $lhs {
551      #[inline]
552      fn $method(&mut self, rhs: $rhs) {
553        let Num(rhs) = rhs;
554        (self.0).$method(rhs)
555      }
556    }
557  };
558}
559
560macro_rules! impl_assign_ops {
561  (impl $imp:ident, $method:ident) => {
562    impl<T> $imp<T> for Num
563    where
564      BigInt: From<T>,
565    {
566      #[inline]
567      fn $method(&mut self, rhs: T) {
568        let rhs = BigRational::from_integer(BigInt::from(rhs));
569        (self.0).$method(rhs)
570      }
571    }
572
573    impl_assign_op!(impl $imp, $method, Num, Num);
574    impl_assign_op!(impl $imp, $method, Num, &Num);
575  };
576}
577
578impl_assign_ops!(impl AddAssign, add_assign);
579impl_assign_ops!(impl SubAssign, sub_assign);
580impl_assign_ops!(impl MulAssign, mul_assign);
581impl_assign_ops!(impl DivAssign, div_assign);
582impl_assign_ops!(impl RemAssign, rem_assign);
583
584
585macro_rules! impl_try_from {
586  ($lhs:ty, $rhs:ty, $to_int:ident) => {
587    impl TryFrom<$rhs> for $lhs {
588      type Error = ();
589
590      fn try_from(other: $rhs) -> Result<Self, Self::Error> {
591        let numer = other.0.numer().$to_int().ok_or(())?;
592        let denom = other.0.denom().$to_int().ok_or(())?;
593
594        Ok(Self::new(numer, denom))
595      }
596    }
597  };
598}
599
600
601impl_num! {
602  /// A fixed size number type with some improvements and customizations
603  /// over [`Rational32`].
604  ///
605  /// Please note that this type is meant to be used mostly in scenarios
606  /// where memory boundedness is of paramount importance. Importantly,
607  /// it does *not* constitute a fully blown replacement for [`Num`], as
608  /// the provided functionality is much more limited (and likely will
609  /// never catch up completely).
610  #[derive(Clone, Copy, Eq, Hash, Ord, PartialEq, PartialOrd)]
611  pub struct Num32(Rational32), i32
612}
613
614impl Num32 {
615  /// Approximate a [`Num`] with a `Num32`.
616  ///
617  /// This constructor provides a potentially lossy way of creating a
618  /// `Num32` that approximates the provided [`Num`].
619  ///
620  /// If you want to make sure that no precision is lost (and fail if
621  /// this constraint cannot be upheld), then usage of
622  /// [`Num32::try_from`] is advised.
623  pub fn approximate(num: Num) -> Self {
624    // If the number can directly be represented as a `Num32` we are
625    // trivially done.
626    if let Ok(num32) = Num32::try_from(&num) {
627      return num32
628    }
629
630    let integer = num.to_integer();
631    // Now if the represented value exceeds the range that we can
632    // represent (either in the positive or the negative) then the best
633    // we can do is to "clamp" the value to the representable min/max.
634    if integer >= BigInt::from(i32::MAX) {
635      Num32::from(i32::MAX)
636    } else if integer <= BigInt::from(i32::MIN) {
637      Num32::from(i32::MIN)
638    } else {
639      // Otherwise use the continued fractions algorithm to calculate
640      // the closest representable approximation.
641      match Self::continued_fractions(num, integer) {
642        Ok(num) | Err(num) => num,
643      }
644    }
645  }
646
647  /// Approximate the provided number using the continued fractions
648  /// algorithm as outlined here:
649  /// https://web.archive.org/web/20120223164926/http://mathforum.org/dr.math/faq/faq.fractions.html#decfrac
650  fn continued_fractions(num: Num, integer: BigInt) -> Result<Num32, Num32> {
651    let mut q = num.0;
652    let mut a = integer;
653    let mut n0 = 0i32;
654    let mut d0 = 1i32;
655    let mut n1 = 1i32;
656    let mut d1 = 0i32;
657
658    loop {
659      if q.is_integer() {
660        break Ok(Num32::new(n1, d1))
661      }
662
663      let a32 = a.to_i32();
664      let n = a32
665        .and_then(|n| n.checked_mul(n1))
666        .and_then(|n| n.checked_add(n0))
667        .ok_or_else(|| Num32::new(n1, d1))?;
668      let d = a32
669        .and_then(|n| n.checked_mul(d1))
670        .and_then(|n| n.checked_add(d0))
671        .ok_or_else(|| Num32::new(n1, d1))?;
672
673      n0 = n1;
674      d0 = d1;
675      n1 = n;
676      d1 = d;
677
678      q = (q - a).recip();
679      a = q.to_integer();
680    }
681  }
682}
683
684impl<T> From<T> for Num32
685where
686  i32: From<T>,
687{
688  #[inline]
689  fn from(val: T) -> Self {
690    Self(Rational32::from(i32::from(val)))
691  }
692}
693
694impl_try_from!(Num32, Num, to_i32);
695impl_try_from!(Num32, &Num, to_i32);
696
697
698impl_num! {
699  /// A fixed size number type with some improvements and customizations
700  /// over [`Rational64`].
701  ///
702  /// Please note that this type is meant to be used mostly in scenarios
703  /// where memory boundedness is of paramount importance. Importantly,
704  /// it does *not* constitute a fully blown replacement for [`Num`], as
705  /// the provided functionality is much more limited (and likely will
706  /// never catch up completely).
707  #[derive(Clone, Copy, Eq, Hash, Ord, PartialEq, PartialOrd)]
708  pub struct Num64(Rational64), i64
709}
710
711impl<T> From<T> for Num64
712where
713  i64: From<T>,
714{
715  #[inline]
716  fn from(val: T) -> Self {
717    Self(Rational64::from(i64::from(val)))
718  }
719}
720
721impl_try_from!(Num64, Num, to_i64);
722impl_try_from!(Num64, &Num, to_i64);