@@ -512,8 +512,8 @@ impl DenominatedAmount {
512512 }
513513 }
514514
515- /// Attempt to increase the precision of an amount . Can fail
516- /// if the resulting amount does not fit into 256 bits.
515+ /// Return an equivalent denominated amount with the given denomination . Can
516+ /// fail if the resulting amount does not fit into 256 bits.
517517 pub fn increase_precision (
518518 self ,
519519 denom : Denomination ,
@@ -534,8 +534,43 @@ impl DenominatedAmount {
534534 . ok_or ( AmountParseError :: PrecisionOverflow )
535535 }
536536
537+ /// Return the closest denominated amount with the given denomination and
538+ /// the error.
539+ pub fn approximate (
540+ self ,
541+ denom : Denomination ,
542+ ) -> Result < ( Self , Self ) , AmountParseError > {
543+ if denom. 0 < self . denom . 0 {
544+ // Divide numerator and denominator by a power of 10
545+ let amount = self . amount . raw_amount ( ) ;
546+ #[ allow( clippy:: arithmetic_side_effects) ]
547+ let ( quot, rem) = Uint :: from ( 10 )
548+ . checked_pow ( Uint :: from ( self . denom . 0 - denom. 0 ) )
549+ . and_then ( |scaling| {
550+ amount. checked_div ( scaling) . zip ( amount. checked_rem ( scaling) )
551+ } )
552+ . ok_or ( AmountParseError :: PrecisionOverflow ) ?;
553+ let approx = Self {
554+ amount : quot. into ( ) ,
555+ denom,
556+ } ;
557+ let error = Self {
558+ amount : rem. into ( ) ,
559+ denom : self . denom ,
560+ } ;
561+ Ok ( ( approx, error) )
562+ } else {
563+ // Multiply numerator and denominator by a power of 10
564+ let error = Self {
565+ amount : 0 . into ( ) ,
566+ denom : self . denom ,
567+ } ;
568+ self . increase_precision ( denom) . map ( |x| ( x, error) )
569+ }
570+ }
571+
537572 /// Create a new [`DenominatedAmount`] with the same underlying
538- /// amout but a new denomination.
573+ /// amount but a new denomination.
539574 pub fn redenominate ( self , new_denom : u8 ) -> Self {
540575 Self {
541576 amount : self . amount ,
@@ -590,6 +625,38 @@ impl DenominatedAmount {
590625 } )
591626 }
592627
628+ /// Checked division computed to the given precision. Returns `None` on
629+ /// overflow.
630+ pub fn checked_div_precision (
631+ & self ,
632+ rhs : DenominatedAmount ,
633+ denom : Denomination ,
634+ ) -> Option < Self > {
635+ #[ allow( clippy:: arithmetic_side_effects) ]
636+ let pow = i16:: from ( rhs. denom . 0 ) + i16:: from ( denom. 0 )
637+ - i16:: from ( self . denom . 0 ) ;
638+ if pow < 0 {
639+ return None ;
640+ }
641+ let amount = Uint :: from ( 10 ) . checked_pow ( Uint :: from ( pow) ) . and_then (
642+ |scaling| {
643+ scaling. checked_mul_div (
644+ self . amount . raw_amount ( ) ,
645+ rhs. amount . raw_amount ( ) ,
646+ )
647+ } ,
648+ ) ?;
649+ Some ( Self {
650+ amount : amount. 0 . into ( ) ,
651+ denom,
652+ } )
653+ }
654+
655+ /// Checked division. Returns `None` on overflow.
656+ pub fn checked_div ( & self , rhs : DenominatedAmount ) -> Option < Self > {
657+ self . checked_div_precision ( rhs, self . denom )
658+ }
659+
593660 /// Returns the significand of this number
594661 pub const fn amount ( & self ) -> Amount {
595662 self . amount
0 commit comments