| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320 |
- package inf
- 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 rounder
- 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 rndr struct {
- useRem bool
- round func(z, quo *Dec, remNum, remDen *big.Int) *Dec
- }
- func (r rndr) UseRemainder() bool {
- return r.useRem
- }
- func (r rndr) 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, 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, 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, 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, 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, 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, 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, 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 = rndr{true,
- func(z, q *Dec, rA, rB *big.Int) *Dec {
- if rA.Sign() != 0 {
- return nil
- }
- return z.Set(q)
- }}
- var roundDown = rndr{false,
- func(z, q *Dec, rA, rB *big.Int) *Dec {
- return z.Set(q)
- }}
- var roundUp = rndr{true,
- func(z, q *Dec, rA, rB *big.Int) *Dec {
- z.Set(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.Set(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 = rndr{true, roundHalf(
- func(c int, odd uint) bool {
- return c > 0
- })}
- var roundHalfUp = rndr{true, roundHalf(
- func(c int, odd uint) bool {
- return c >= 0
- })}
- var roundHalfEven = rndr{true, roundHalf(
- func(c int, odd uint) bool {
- return c > 0 || c == 0 && odd == 1
- })}
- var roundFloor = rndr{true,
- func(z, q *Dec, rA, rB *big.Int) *Dec {
- z.Set(q)
- if rA.Sign()*rB.Sign() < 0 {
- z.Unscaled().Add(z.Unscaled(), intSign[0])
- }
- return z
- }}
- var roundCeil = rndr{true,
- func(z, q *Dec, rA, rB *big.Int) *Dec {
- z.Set(q)
- if rA.Sign()*rB.Sign() > 0 {
- z.Unscaled().Add(z.Unscaled(), intSign[2])
- }
- return z
- }}
|