...

Source file src/gopkg.in/inf.v0/rounder.go

Documentation: gopkg.in/inf.v0

     1  package inf
     2  
     3  import (
     4  	"math/big"
     5  )
     6  
     7  // Rounder represents a method for rounding the (possibly infinite decimal)
     8  // result of a division to a finite Dec. It is used by Dec.Round() and
     9  // Dec.Quo().
    10  //
    11  // See the Example for results of using each Rounder with some sample values.
    12  //
    13  type Rounder rounder
    14  
    15  // See http://speleotrove.com/decimal/damodel.html#refround for more detailed
    16  // definitions of these rounding modes.
    17  var (
    18  	RoundDown     Rounder // towards 0
    19  	RoundUp       Rounder // away from 0
    20  	RoundFloor    Rounder // towards -infinity
    21  	RoundCeil     Rounder // towards +infinity
    22  	RoundHalfDown Rounder // to nearest; towards 0 if same distance
    23  	RoundHalfUp   Rounder // to nearest; away from 0 if same distance
    24  	RoundHalfEven Rounder // to nearest; even last digit if same distance
    25  )
    26  
    27  // RoundExact is to be used in the case when rounding is not necessary.
    28  // When used with Quo or Round, it returns the result verbatim when it can be
    29  // expressed exactly with the given precision, and it returns nil otherwise.
    30  // QuoExact is a shorthand for using Quo with RoundExact.
    31  var RoundExact Rounder
    32  
    33  type rounder interface {
    34  
    35  	// When UseRemainder() returns true, the Round() method is passed the
    36  	// remainder of the division, expressed as the numerator and denominator of
    37  	// a rational.
    38  	UseRemainder() bool
    39  
    40  	// Round sets the rounded value of a quotient to z, and returns z.
    41  	// quo is rounded down (truncated towards zero) to the scale obtained from
    42  	// the Scaler in Quo().
    43  	//
    44  	// When the remainder is not used, remNum and remDen are nil.
    45  	// When used, the remainder is normalized between -1 and 1; that is:
    46  	//
    47  	//  -|remDen| < remNum < |remDen|
    48  	//
    49  	// remDen has the same sign as y, and remNum is zero or has the same sign
    50  	// as x.
    51  	Round(z, quo *Dec, remNum, remDen *big.Int) *Dec
    52  }
    53  
    54  type rndr struct {
    55  	useRem bool
    56  	round  func(z, quo *Dec, remNum, remDen *big.Int) *Dec
    57  }
    58  
    59  func (r rndr) UseRemainder() bool {
    60  	return r.useRem
    61  }
    62  
    63  func (r rndr) Round(z, quo *Dec, remNum, remDen *big.Int) *Dec {
    64  	return r.round(z, quo, remNum, remDen)
    65  }
    66  
    67  var intSign = []*big.Int{big.NewInt(-1), big.NewInt(0), big.NewInt(1)}
    68  
    69  func roundHalf(f func(c int, odd uint) (roundUp bool)) func(z, q *Dec, rA, rB *big.Int) *Dec {
    70  	return func(z, q *Dec, rA, rB *big.Int) *Dec {
    71  		z.Set(q)
    72  		brA, brB := rA.BitLen(), rB.BitLen()
    73  		if brA < brB-1 {
    74  			// brA < brB-1 => |rA| < |rB/2|
    75  			return z
    76  		}
    77  		roundUp := false
    78  		srA, srB := rA.Sign(), rB.Sign()
    79  		s := srA * srB
    80  		if brA == brB-1 {
    81  			rA2 := new(big.Int).Lsh(rA, 1)
    82  			if s < 0 {
    83  				rA2.Neg(rA2)
    84  			}
    85  			roundUp = f(rA2.Cmp(rB)*srB, z.UnscaledBig().Bit(0))
    86  		} else {
    87  			// brA > brB-1 => |rA| > |rB/2|
    88  			roundUp = true
    89  		}
    90  		if roundUp {
    91  			z.UnscaledBig().Add(z.UnscaledBig(), intSign[s+1])
    92  		}
    93  		return z
    94  	}
    95  }
    96  
    97  func init() {
    98  	RoundExact = rndr{true,
    99  		func(z, q *Dec, rA, rB *big.Int) *Dec {
   100  			if rA.Sign() != 0 {
   101  				return nil
   102  			}
   103  			return z.Set(q)
   104  		}}
   105  	RoundDown = rndr{false,
   106  		func(z, q *Dec, rA, rB *big.Int) *Dec {
   107  			return z.Set(q)
   108  		}}
   109  	RoundUp = rndr{true,
   110  		func(z, q *Dec, rA, rB *big.Int) *Dec {
   111  			z.Set(q)
   112  			if rA.Sign() != 0 {
   113  				z.UnscaledBig().Add(z.UnscaledBig(), intSign[rA.Sign()*rB.Sign()+1])
   114  			}
   115  			return z
   116  		}}
   117  	RoundFloor = rndr{true,
   118  		func(z, q *Dec, rA, rB *big.Int) *Dec {
   119  			z.Set(q)
   120  			if rA.Sign()*rB.Sign() < 0 {
   121  				z.UnscaledBig().Add(z.UnscaledBig(), intSign[0])
   122  			}
   123  			return z
   124  		}}
   125  	RoundCeil = rndr{true,
   126  		func(z, q *Dec, rA, rB *big.Int) *Dec {
   127  			z.Set(q)
   128  			if rA.Sign()*rB.Sign() > 0 {
   129  				z.UnscaledBig().Add(z.UnscaledBig(), intSign[2])
   130  			}
   131  			return z
   132  		}}
   133  	RoundHalfDown = rndr{true, roundHalf(
   134  		func(c int, odd uint) bool {
   135  			return c > 0
   136  		})}
   137  	RoundHalfUp = rndr{true, roundHalf(
   138  		func(c int, odd uint) bool {
   139  			return c >= 0
   140  		})}
   141  	RoundHalfEven = rndr{true, roundHalf(
   142  		func(c int, odd uint) bool {
   143  			return c > 0 || c == 0 && odd == 1
   144  		})}
   145  }
   146  

View as plain text