mirror of
				https://gitea.com/Lydanne/buildx.git
				synced 2025-11-04 01:53:42 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			146 lines
		
	
	
		
			3.8 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			146 lines
		
	
	
		
			3.8 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
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
 | 
						|
		})}
 | 
						|
}
 |