You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
145 lines
3.8 KiB
145 lines
3.8 KiB
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(). |
|
// |
|
// See the Example for results of using each Rounder with some sample values. |
|
// |
|
type Rounder rounder |
|
|
|
// See http://speleotrove.com/decimal/damodel.html#refround for more detailed |
|
// definitions of these rounding modes. |
|
var ( |
|
RoundDown Rounder // towards 0 |
|
RoundUp Rounder // away from 0 |
|
RoundFloor Rounder // towards -infinity |
|
RoundCeil Rounder // towards +infinity |
|
RoundHalfDown Rounder // to nearest; towards 0 if same distance |
|
RoundHalfUp Rounder // to nearest; away from 0 if same distance |
|
RoundHalfEven Rounder // to nearest; even last digit if same distance |
|
) |
|
|
|
// RoundExact is to be used in the case when rounding is not necessary. |
|
// When used with Quo or Round, it returns the result verbatim when it can be |
|
// expressed exactly with the given precision, and it returns nil otherwise. |
|
// QuoExact is a shorthand for using Quo with RoundExact. |
|
var RoundExact 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) |
|
} |
|
|
|
var intSign = []*big.Int{big.NewInt(-1), big.NewInt(0), big.NewInt(1)} |
|
|
|
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.UnscaledBig().Bit(0)) |
|
} else { |
|
// brA > brB-1 => |rA| > |rB/2| |
|
roundUp = true |
|
} |
|
if roundUp { |
|
z.UnscaledBig().Add(z.UnscaledBig(), intSign[s+1]) |
|
} |
|
return z |
|
} |
|
} |
|
|
|
func init() { |
|
RoundExact = rndr{true, |
|
func(z, q *Dec, rA, rB *big.Int) *Dec { |
|
if rA.Sign() != 0 { |
|
return nil |
|
} |
|
return z.Set(q) |
|
}} |
|
RoundDown = rndr{false, |
|
func(z, q *Dec, rA, rB *big.Int) *Dec { |
|
return z.Set(q) |
|
}} |
|
RoundUp = rndr{true, |
|
func(z, q *Dec, rA, rB *big.Int) *Dec { |
|
z.Set(q) |
|
if rA.Sign() != 0 { |
|
z.UnscaledBig().Add(z.UnscaledBig(), intSign[rA.Sign()*rB.Sign()+1]) |
|
} |
|
return z |
|
}} |
|
RoundFloor = rndr{true, |
|
func(z, q *Dec, rA, rB *big.Int) *Dec { |
|
z.Set(q) |
|
if rA.Sign()*rB.Sign() < 0 { |
|
z.UnscaledBig().Add(z.UnscaledBig(), intSign[0]) |
|
} |
|
return z |
|
}} |
|
RoundCeil = rndr{true, |
|
func(z, q *Dec, rA, rB *big.Int) *Dec { |
|
z.Set(q) |
|
if rA.Sign()*rB.Sign() > 0 { |
|
z.UnscaledBig().Add(z.UnscaledBig(), intSign[2]) |
|
} |
|
return z |
|
}} |
|
RoundHalfDown = rndr{true, roundHalf( |
|
func(c int, odd uint) bool { |
|
return c > 0 |
|
})} |
|
RoundHalfUp = rndr{true, roundHalf( |
|
func(c int, odd uint) bool { |
|
return c >= 0 |
|
})} |
|
RoundHalfEven = rndr{true, roundHalf( |
|
func(c int, odd uint) bool { |
|
return c > 0 || c == 0 && odd == 1 |
|
})} |
|
}
|
|
|