package inf // This file implements signed multi-precision decimals. import ( "math/big" ) // Rounder represents a method for rounding the (possibly infinite decimal) // result of a division to a finite Dec. It is used by Dec.Round() and // Dec.Quo(). // type Rounder interface { // When UseRemainder() returns true, the Round() method is passed the // remainder of the division, expressed as the numerator and denominator of // a rational. UseRemainder() bool // Round sets the rounded value of a quotient to z, and returns z. // quo is rounded down (truncated towards zero) to the scale obtained from // the Scaler in Quo(). // // When the remainder is not used, remNum and remDen are nil. // When used, the remainder is normalized between -1 and 1; that is: // // -|remDen| < remNum < |remDen| // // remDen has the same sign as y, and remNum is zero or has the same sign // as x. Round(z, quo *Dec, remNum, remDen *big.Int) *Dec } type rounder struct { useRem bool round func(z, quo *Dec, remNum, remDen *big.Int) *Dec } func (r rounder) UseRemainder() bool { return r.useRem } func (r rounder) Round(z, quo *Dec, remNum, remDen *big.Int) *Dec { return r.round(z, quo, remNum, remDen) } // RoundExact returns quo if rem is zero, or nil otherwise. It is intended to // be used with ScaleQuoExact when it is guaranteed that the result can be // obtained without rounding. QuoExact is a shorthand for such a quotient // operation. // var RoundExact Rounder = roundExact // RoundDown rounds towards 0; that is, returns the Dec with the greatest // absolute value not exceeding that of the result represented by quo and rem. // // The following table shows examples of the results for // Quo(x, y, Scale(scale), RoundDown). // // x y scale result // ------------------------------ // -1.8 10 1 -0.1 // -1.5 10 1 -0.1 // -1.2 10 1 -0.1 // -1.0 10 1 -0.1 // -0.8 10 1 -0.0 // -0.5 10 1 -0.0 // -0.2 10 1 -0.0 // 0.0 10 1 0.0 // 0.2 10 1 0.0 // 0.5 10 1 0.0 // 0.8 10 1 0.0 // 1.0 10 1 0.1 // 1.2 10 1 0.1 // 1.5 10 1 0.1 // 1.8 10 1 0.1 // var RoundDown Rounder = roundDown // RoundUp rounds away from 0; that is, returns the Dec with the smallest // absolute value not smaller than that of the result represented by quo and // rem. // // The following table shows examples of the results for // Quo(x, y, Scale(scale), RoundUp). // // x y scale result // ------------------------------ // -1.8 10 1 -0.2 // -1.5 10 1 -0.2 // -1.2 10 1 -0.2 // -1.0 10 1 -0.1 // -0.8 10 1 -0.1 // -0.5 10 1 -0.1 // -0.2 10 1 -0.1 // 0.0 10 1 0.0 // 0.2 10 1 0.1 // 0.5 10 1 0.1 // 0.8 10 1 0.1 // 1.0 10 1 0.1 // 1.2 10 1 0.2 // 1.5 10 1 0.2 // 1.8 10 1 0.2 // var RoundUp Rounder = roundUp // RoundHalfDown rounds to the nearest Dec, and when the remainder is 1/2, it // rounds to the Dec with the lower absolute value. // // The following table shows examples of the results for // Quo(x, y, Scale(scale), RoundHalfDown). // // x y scale result // ------------------------------ // -1.8 10 1 -0.2 // -1.5 10 1 -0.1 // -1.2 10 1 -0.1 // -1.0 10 1 -0.1 // -0.8 10 1 -0.1 // -0.5 10 1 -0.0 // -0.2 10 1 -0.0 // 0.0 10 1 0.0 // 0.2 10 1 0.0 // 0.5 10 1 0.0 // 0.8 10 1 0.1 // 1.0 10 1 0.1 // 1.2 10 1 0.1 // 1.5 10 1 0.1 // 1.8 10 1 0.2 // var RoundHalfDown Rounder = roundHalfDown // RoundHalfUp rounds to the nearest Dec, and when the remainder is 1/2, it // rounds to the Dec with the greater absolute value. // // The following table shows examples of the results for // Quo(x, y, Scale(scale), RoundHalfUp). // // x y scale result // ------------------------------ // -1.8 10 1 -0.2 // -1.5 10 1 -0.2 // -1.2 10 1 -0.1 // -1.0 10 1 -0.1 // -0.8 10 1 -0.1 // -0.5 10 1 -0.1 // -0.2 10 1 -0.0 // 0.0 10 1 0.0 // 0.2 10 1 0.0 // 0.5 10 1 0.1 // 0.8 10 1 0.1 // 1.0 10 1 0.1 // 1.2 10 1 0.1 // 1.5 10 1 0.2 // 1.8 10 1 0.2 // var RoundHalfUp Rounder = roundHalfUp // RoundHalfEven rounds to the nearest Dec, and when the remainder is 1/2, it // rounds to the Dec with even last digit. // // The following table shows examples of the results for // Quo(x, y, Scale(scale), RoundHalfEven). // // x y scale result // ------------------------------ // -1.8 10 1 -0.2 // -1.5 10 1 -0.2 // -1.2 10 1 -0.1 // -1.0 10 1 -0.1 // -0.8 10 1 -0.1 // -0.5 10 1 -0.0 // -0.2 10 1 -0.0 // 0.0 10 1 0.0 // 0.2 10 1 0.0 // 0.5 10 1 0.0 // 0.8 10 1 0.1 // 1.0 10 1 0.1 // 1.2 10 1 0.1 // 1.5 10 1 0.2 // 1.8 10 1 0.2 // var RoundHalfEven Rounder = roundHalfEven // RoundFloor rounds towards negative infinity; that is, returns the greatest // Dec not exceeding the result represented by quo and rem. // // The following table shows examples of the results for // Quo(x, y, Scale(scale), RoundFloor). // // x y scale result // ------------------------------ // -1.8 10 1 -0.2 // -1.5 10 1 -0.2 // -1.2 10 1 -0.2 // -1.0 10 1 -0.1 // -0.8 10 1 -0.1 // -0.5 10 1 -0.1 // -0.2 10 1 -0.1 // 0.0 10 1 0.0 // 0.2 10 1 0.0 // 0.5 10 1 0.0 // 0.8 10 1 0.0 // 1.0 10 1 0.1 // 1.2 10 1 0.1 // 1.5 10 1 0.1 // 1.8 10 1 0.1 // var RoundFloor Rounder = roundFloor // RoundCeil rounds towards positive infinity; that is, returns the // smallest Dec not smaller than the result represented by quo and rem. // // The following table shows examples of the results for // Quo(x, y, Scale(scale), RoundCeil). // // x y scale result // ------------------------------ // -1.8 10 1 -0.1 // -1.5 10 1 -0.1 // -1.2 10 1 -0.1 // -1.0 10 1 -0.1 // -0.8 10 1 -0.0 // -0.5 10 1 -0.0 // -0.2 10 1 -0.0 // 0.0 10 1 0.0 // 0.2 10 1 0.1 // 0.5 10 1 0.1 // 0.8 10 1 0.1 // 1.0 10 1 0.1 // 1.2 10 1 0.2 // 1.5 10 1 0.2 // 1.8 10 1 0.2 // var RoundCeil Rounder = roundCeil var intSign = []*big.Int{big.NewInt(-1), big.NewInt(0), big.NewInt(1)} var roundExact = rounder{true, func(z, q *Dec, rA, rB *big.Int) *Dec { if rA.Sign() != 0 { return nil } return z.move(q) }} var roundDown = rounder{false, func(z, q *Dec, rA, rB *big.Int) *Dec { return z.move(q) }} var roundUp = rounder{true, func(z, q *Dec, rA, rB *big.Int) *Dec { z.move(q) if rA.Sign() != 0 { z.Unscaled().Add(z.Unscaled(), intSign[rA.Sign()*rB.Sign()+1]) } return z }} func roundHalf(f func(c int, odd uint) (roundUp bool)) func(z, q *Dec, rA, rB *big.Int) *Dec { return func(z, q *Dec, rA, rB *big.Int) *Dec { z.move(q) brA, brB := rA.BitLen(), rB.BitLen() if brA < brB-1 { // brA < brB-1 => |rA| < |rB/2| return z } roundUp := false srA, srB := rA.Sign(), rB.Sign() s := srA * srB if brA == brB-1 { rA2 := new(big.Int).Lsh(rA, 1) if s < 0 { rA2.Neg(rA2) } roundUp = f(rA2.Cmp(rB)*srB, z.Unscaled().Bit(0)) } else { // brA > brB-1 => |rA| > |rB/2| roundUp = true } if roundUp { z.Unscaled().Add(z.Unscaled(), intSign[s+1]) } return z } } var roundHalfDown = rounder{true, roundHalf( func(c int, odd uint) bool { return c > 0 })} var roundHalfUp = rounder{true, roundHalf( func(c int, odd uint) bool { return c >= 0 })} var roundHalfEven = rounder{true, roundHalf( func(c int, odd uint) bool { return c > 0 || c == 0 && odd == 1 })} var roundFloor = rounder{true, func(z, q *Dec, rA, rB *big.Int) *Dec { z.move(q) if rA.Sign()*rB.Sign() < 0 { z.Unscaled().Add(z.Unscaled(), intSign[0]) } return z }} var roundCeil = rounder{true, func(z, q *Dec, rA, rB *big.Int) *Dec { z.move(q) if rA.Sign()*rB.Sign() > 0 { z.Unscaled().Add(z.Unscaled(), intSign[2]) } return z }}