1478 lines
39 KiB
Go
1478 lines
39 KiB
Go
// Package decimal implements an arbitrary precision fixed-point decimal.
|
|
//
|
|
// The zero-value of a Decimal is 0, as you would expect.
|
|
//
|
|
// The best way to create a new Decimal is to use decimal.NewFromString, ex:
|
|
//
|
|
// n, err := decimal.NewFromString("-123.4567")
|
|
// n.String() // output: "-123.4567"
|
|
//
|
|
// To use Decimal as part of a struct:
|
|
//
|
|
// type Struct struct {
|
|
// Number Decimal
|
|
// }
|
|
//
|
|
// Note: This can "only" represent numbers with a maximum of 2^31 digits after the decimal point.
|
|
package decimal
|
|
|
|
import (
|
|
"database/sql/driver"
|
|
"encoding/binary"
|
|
"fmt"
|
|
"math"
|
|
"math/big"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
// DivisionPrecision is the number of decimal places in the result when it
|
|
// doesn't divide exactly.
|
|
//
|
|
// Example:
|
|
//
|
|
// d1 := decimal.NewFromFloat(2).Div(decimal.NewFromFloat(3))
|
|
// d1.String() // output: "0.6666666666666667"
|
|
// d2 := decimal.NewFromFloat(2).Div(decimal.NewFromFloat(30000))
|
|
// d2.String() // output: "0.0000666666666667"
|
|
// d3 := decimal.NewFromFloat(20000).Div(decimal.NewFromFloat(3))
|
|
// d3.String() // output: "6666.6666666666666667"
|
|
// decimal.DivisionPrecision = 3
|
|
// d4 := decimal.NewFromFloat(2).Div(decimal.NewFromFloat(3))
|
|
// d4.String() // output: "0.667"
|
|
//
|
|
var DivisionPrecision = 16
|
|
|
|
// MarshalJSONWithoutQuotes should be set to true if you want the decimal to
|
|
// be JSON marshaled as a number, instead of as a string.
|
|
// WARNING: this is dangerous for decimals with many digits, since many JSON
|
|
// unmarshallers (ex: Javascript's) will unmarshal JSON numbers to IEEE 754
|
|
// double-precision floating point numbers, which means you can potentially
|
|
// silently lose precision.
|
|
var MarshalJSONWithoutQuotes = false
|
|
|
|
// Zero constant, to make computations faster.
|
|
// Zero should never be compared with == or != directly, please use decimal.Equal or decimal.Cmp instead.
|
|
var Zero = New(0, 1)
|
|
|
|
var zeroInt = big.NewInt(0)
|
|
var oneInt = big.NewInt(1)
|
|
var twoInt = big.NewInt(2)
|
|
var fourInt = big.NewInt(4)
|
|
var fiveInt = big.NewInt(5)
|
|
var tenInt = big.NewInt(10)
|
|
var twentyInt = big.NewInt(20)
|
|
|
|
// Decimal represents a fixed-point decimal. It is immutable.
|
|
// number = value * 10 ^ exp
|
|
type Decimal struct {
|
|
value *big.Int
|
|
|
|
// NOTE(vadim): this must be an int32, because we cast it to float64 during
|
|
// calculations. If exp is 64 bit, we might lose precision.
|
|
// If we cared about being able to represent every possible decimal, we
|
|
// could make exp a *big.Int but it would hurt performance and numbers
|
|
// like that are unrealistic.
|
|
exp int32
|
|
}
|
|
|
|
// New returns a new fixed-point decimal, value * 10 ^ exp.
|
|
func New(value int64, exp int32) Decimal {
|
|
return Decimal{
|
|
value: big.NewInt(value),
|
|
exp: exp,
|
|
}
|
|
}
|
|
|
|
// NewFromInt converts a int64 to Decimal.
|
|
//
|
|
// Example:
|
|
//
|
|
// NewFromInt(123).String() // output: "123"
|
|
// NewFromInt(-10).String() // output: "-10"
|
|
func NewFromInt(value int64) Decimal {
|
|
return Decimal{
|
|
value: big.NewInt(value),
|
|
exp: 0,
|
|
}
|
|
}
|
|
|
|
// NewFromInt32 converts a int32 to Decimal.
|
|
//
|
|
// Example:
|
|
//
|
|
// NewFromInt(123).String() // output: "123"
|
|
// NewFromInt(-10).String() // output: "-10"
|
|
func NewFromInt32(value int32) Decimal {
|
|
return Decimal{
|
|
value: big.NewInt(int64(value)),
|
|
exp: 0,
|
|
}
|
|
}
|
|
|
|
// NewFromBigInt returns a new Decimal from a big.Int, value * 10 ^ exp
|
|
func NewFromBigInt(value *big.Int, exp int32) Decimal {
|
|
return Decimal{
|
|
value: big.NewInt(0).Set(value),
|
|
exp: exp,
|
|
}
|
|
}
|
|
|
|
// NewFromString returns a new Decimal from a string representation.
|
|
// Trailing zeroes are not trimmed.
|
|
//
|
|
// Example:
|
|
//
|
|
// d, err := NewFromString("-123.45")
|
|
// d2, err := NewFromString(".0001")
|
|
// d3, err := NewFromString("1.47000")
|
|
//
|
|
func NewFromString(value string) (Decimal, error) {
|
|
originalInput := value
|
|
var intString string
|
|
var exp int64
|
|
|
|
// Check if number is using scientific notation
|
|
eIndex := strings.IndexAny(value, "Ee")
|
|
if eIndex != -1 {
|
|
expInt, err := strconv.ParseInt(value[eIndex+1:], 10, 32)
|
|
if err != nil {
|
|
if e, ok := err.(*strconv.NumError); ok && e.Err == strconv.ErrRange {
|
|
return Decimal{}, fmt.Errorf("can't convert %s to decimal: fractional part too long", value)
|
|
}
|
|
return Decimal{}, fmt.Errorf("can't convert %s to decimal: exponent is not numeric", value)
|
|
}
|
|
value = value[:eIndex]
|
|
exp = expInt
|
|
}
|
|
|
|
parts := strings.Split(value, ".")
|
|
if len(parts) == 1 {
|
|
// There is no decimal point, we can just parse the original string as
|
|
// an int
|
|
intString = value
|
|
} else if len(parts) == 2 {
|
|
intString = parts[0] + parts[1]
|
|
expInt := -len(parts[1])
|
|
exp += int64(expInt)
|
|
} else {
|
|
return Decimal{}, fmt.Errorf("can't convert %s to decimal: too many .s", value)
|
|
}
|
|
|
|
dValue := new(big.Int)
|
|
_, ok := dValue.SetString(intString, 10)
|
|
if !ok {
|
|
return Decimal{}, fmt.Errorf("can't convert %s to decimal", value)
|
|
}
|
|
|
|
if exp < math.MinInt32 || exp > math.MaxInt32 {
|
|
// NOTE(vadim): I doubt a string could realistically be this long
|
|
return Decimal{}, fmt.Errorf("can't convert %s to decimal: fractional part too long", originalInput)
|
|
}
|
|
|
|
return Decimal{
|
|
value: dValue,
|
|
exp: int32(exp),
|
|
}, nil
|
|
}
|
|
|
|
// RequireFromString returns a new Decimal from a string representation
|
|
// or panics if NewFromString would have returned an error.
|
|
//
|
|
// Example:
|
|
//
|
|
// d := RequireFromString("-123.45")
|
|
// d2 := RequireFromString(".0001")
|
|
//
|
|
func RequireFromString(value string) Decimal {
|
|
dec, err := NewFromString(value)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
return dec
|
|
}
|
|
|
|
// NewFromFloat converts a float64 to Decimal.
|
|
//
|
|
// The converted number will contain the number of significant digits that can be
|
|
// represented in a float with reliable roundtrip.
|
|
// This is typically 15 digits, but may be more in some cases.
|
|
// See https://www.exploringbinary.com/decimal-precision-of-binary-floating-point-numbers/ for more information.
|
|
//
|
|
// For slightly faster conversion, use NewFromFloatWithExponent where you can specify the precision in absolute terms.
|
|
//
|
|
// NOTE: this will panic on NaN, +/-inf
|
|
func NewFromFloat(value float64) Decimal {
|
|
if value == 0 {
|
|
return New(0, 0)
|
|
}
|
|
return newFromFloat(value, math.Float64bits(value), &float64info)
|
|
}
|
|
|
|
// NewFromFloat32 converts a float32 to Decimal.
|
|
//
|
|
// The converted number will contain the number of significant digits that can be
|
|
// represented in a float with reliable roundtrip.
|
|
// This is typically 6-8 digits depending on the input.
|
|
// See https://www.exploringbinary.com/decimal-precision-of-binary-floating-point-numbers/ for more information.
|
|
//
|
|
// For slightly faster conversion, use NewFromFloatWithExponent where you can specify the precision in absolute terms.
|
|
//
|
|
// NOTE: this will panic on NaN, +/-inf
|
|
func NewFromFloat32(value float32) Decimal {
|
|
if value == 0 {
|
|
return New(0, 0)
|
|
}
|
|
// XOR is workaround for https://github.com/golang/go/issues/26285
|
|
a := math.Float32bits(value) ^ 0x80808080
|
|
return newFromFloat(float64(value), uint64(a)^0x80808080, &float32info)
|
|
}
|
|
|
|
func newFromFloat(val float64, bits uint64, flt *floatInfo) Decimal {
|
|
if math.IsNaN(val) || math.IsInf(val, 0) {
|
|
panic(fmt.Sprintf("Cannot create a Decimal from %v", val))
|
|
}
|
|
exp := int(bits>>flt.mantbits) & (1<<flt.expbits - 1)
|
|
mant := bits & (uint64(1)<<flt.mantbits - 1)
|
|
|
|
switch exp {
|
|
case 0:
|
|
// denormalized
|
|
exp++
|
|
|
|
default:
|
|
// add implicit top bit
|
|
mant |= uint64(1) << flt.mantbits
|
|
}
|
|
exp += flt.bias
|
|
|
|
var d decimal
|
|
d.Assign(mant)
|
|
d.Shift(exp - int(flt.mantbits))
|
|
d.neg = bits>>(flt.expbits+flt.mantbits) != 0
|
|
|
|
roundShortest(&d, mant, exp, flt)
|
|
// If less than 19 digits, we can do calculation in an int64.
|
|
if d.nd < 19 {
|
|
tmp := int64(0)
|
|
m := int64(1)
|
|
for i := d.nd - 1; i >= 0; i-- {
|
|
tmp += m * int64(d.d[i]-'0')
|
|
m *= 10
|
|
}
|
|
if d.neg {
|
|
tmp *= -1
|
|
}
|
|
return Decimal{value: big.NewInt(tmp), exp: int32(d.dp) - int32(d.nd)}
|
|
}
|
|
dValue := new(big.Int)
|
|
dValue, ok := dValue.SetString(string(d.d[:d.nd]), 10)
|
|
if ok {
|
|
return Decimal{value: dValue, exp: int32(d.dp) - int32(d.nd)}
|
|
}
|
|
|
|
return NewFromFloatWithExponent(val, int32(d.dp)-int32(d.nd))
|
|
}
|
|
|
|
// NewFromFloatWithExponent converts a float64 to Decimal, with an arbitrary
|
|
// number of fractional digits.
|
|
//
|
|
// Example:
|
|
//
|
|
// NewFromFloatWithExponent(123.456, -2).String() // output: "123.46"
|
|
//
|
|
func NewFromFloatWithExponent(value float64, exp int32) Decimal {
|
|
if math.IsNaN(value) || math.IsInf(value, 0) {
|
|
panic(fmt.Sprintf("Cannot create a Decimal from %v", value))
|
|
}
|
|
|
|
bits := math.Float64bits(value)
|
|
mant := bits & (1<<52 - 1)
|
|
exp2 := int32((bits >> 52) & (1<<11 - 1))
|
|
sign := bits >> 63
|
|
|
|
if exp2 == 0 {
|
|
// specials
|
|
if mant == 0 {
|
|
return Decimal{}
|
|
}
|
|
// subnormal
|
|
exp2++
|
|
} else {
|
|
// normal
|
|
mant |= 1 << 52
|
|
}
|
|
|
|
exp2 -= 1023 + 52
|
|
|
|
// normalizing base-2 values
|
|
for mant&1 == 0 {
|
|
mant = mant >> 1
|
|
exp2++
|
|
}
|
|
|
|
// maximum number of fractional base-10 digits to represent 2^N exactly cannot be more than -N if N<0
|
|
if exp < 0 && exp < exp2 {
|
|
if exp2 < 0 {
|
|
exp = exp2
|
|
} else {
|
|
exp = 0
|
|
}
|
|
}
|
|
|
|
// representing 10^M * 2^N as 5^M * 2^(M+N)
|
|
exp2 -= exp
|
|
|
|
temp := big.NewInt(1)
|
|
dMant := big.NewInt(int64(mant))
|
|
|
|
// applying 5^M
|
|
if exp > 0 {
|
|
temp = temp.SetInt64(int64(exp))
|
|
temp = temp.Exp(fiveInt, temp, nil)
|
|
} else if exp < 0 {
|
|
temp = temp.SetInt64(-int64(exp))
|
|
temp = temp.Exp(fiveInt, temp, nil)
|
|
dMant = dMant.Mul(dMant, temp)
|
|
temp = temp.SetUint64(1)
|
|
}
|
|
|
|
// applying 2^(M+N)
|
|
if exp2 > 0 {
|
|
dMant = dMant.Lsh(dMant, uint(exp2))
|
|
} else if exp2 < 0 {
|
|
temp = temp.Lsh(temp, uint(-exp2))
|
|
}
|
|
|
|
// rounding and downscaling
|
|
if exp > 0 || exp2 < 0 {
|
|
halfDown := new(big.Int).Rsh(temp, 1)
|
|
dMant = dMant.Add(dMant, halfDown)
|
|
dMant = dMant.Quo(dMant, temp)
|
|
}
|
|
|
|
if sign == 1 {
|
|
dMant = dMant.Neg(dMant)
|
|
}
|
|
|
|
return Decimal{
|
|
value: dMant,
|
|
exp: exp,
|
|
}
|
|
}
|
|
|
|
// rescale returns a rescaled version of the decimal. Returned
|
|
// decimal may be less precise if the given exponent is bigger
|
|
// than the initial exponent of the Decimal.
|
|
// NOTE: this will truncate, NOT round
|
|
//
|
|
// Example:
|
|
//
|
|
// d := New(12345, -4)
|
|
// d2 := d.rescale(-1)
|
|
// d3 := d2.rescale(-4)
|
|
// println(d1)
|
|
// println(d2)
|
|
// println(d3)
|
|
//
|
|
// Output:
|
|
//
|
|
// 1.2345
|
|
// 1.2
|
|
// 1.2000
|
|
//
|
|
func (d Decimal) rescale(exp int32) Decimal {
|
|
d.ensureInitialized()
|
|
|
|
if d.exp == exp {
|
|
return Decimal{
|
|
new(big.Int).Set(d.value),
|
|
d.exp,
|
|
}
|
|
}
|
|
|
|
// NOTE(vadim): must convert exps to float64 before - to prevent overflow
|
|
diff := math.Abs(float64(exp) - float64(d.exp))
|
|
value := new(big.Int).Set(d.value)
|
|
|
|
expScale := new(big.Int).Exp(tenInt, big.NewInt(int64(diff)), nil)
|
|
if exp > d.exp {
|
|
value = value.Quo(value, expScale)
|
|
} else if exp < d.exp {
|
|
value = value.Mul(value, expScale)
|
|
}
|
|
|
|
return Decimal{
|
|
value: value,
|
|
exp: exp,
|
|
}
|
|
}
|
|
|
|
// Abs returns the absolute value of the decimal.
|
|
func (d Decimal) Abs() Decimal {
|
|
d.ensureInitialized()
|
|
d2Value := new(big.Int).Abs(d.value)
|
|
return Decimal{
|
|
value: d2Value,
|
|
exp: d.exp,
|
|
}
|
|
}
|
|
|
|
// Add returns d + d2.
|
|
func (d Decimal) Add(d2 Decimal) Decimal {
|
|
rd, rd2 := RescalePair(d, d2)
|
|
|
|
d3Value := new(big.Int).Add(rd.value, rd2.value)
|
|
return Decimal{
|
|
value: d3Value,
|
|
exp: rd.exp,
|
|
}
|
|
}
|
|
|
|
// Sub returns d - d2.
|
|
func (d Decimal) Sub(d2 Decimal) Decimal {
|
|
rd, rd2 := RescalePair(d, d2)
|
|
|
|
d3Value := new(big.Int).Sub(rd.value, rd2.value)
|
|
return Decimal{
|
|
value: d3Value,
|
|
exp: rd.exp,
|
|
}
|
|
}
|
|
|
|
// Neg returns -d.
|
|
func (d Decimal) Neg() Decimal {
|
|
d.ensureInitialized()
|
|
val := new(big.Int).Neg(d.value)
|
|
return Decimal{
|
|
value: val,
|
|
exp: d.exp,
|
|
}
|
|
}
|
|
|
|
// Mul returns d * d2.
|
|
func (d Decimal) Mul(d2 Decimal) Decimal {
|
|
d.ensureInitialized()
|
|
d2.ensureInitialized()
|
|
|
|
expInt64 := int64(d.exp) + int64(d2.exp)
|
|
if expInt64 > math.MaxInt32 || expInt64 < math.MinInt32 {
|
|
// NOTE(vadim): better to panic than give incorrect results, as
|
|
// Decimals are usually used for money
|
|
panic(fmt.Sprintf("exponent %v overflows an int32!", expInt64))
|
|
}
|
|
|
|
d3Value := new(big.Int).Mul(d.value, d2.value)
|
|
return Decimal{
|
|
value: d3Value,
|
|
exp: int32(expInt64),
|
|
}
|
|
}
|
|
|
|
// Shift shifts the decimal in base 10.
|
|
// It shifts left when shift is positive and right if shift is negative.
|
|
// In simpler terms, the given value for shift is added to the exponent
|
|
// of the decimal.
|
|
func (d Decimal) Shift(shift int32) Decimal {
|
|
d.ensureInitialized()
|
|
return Decimal{
|
|
value: new(big.Int).Set(d.value),
|
|
exp: d.exp + shift,
|
|
}
|
|
}
|
|
|
|
// Div returns d / d2. If it doesn't divide exactly, the result will have
|
|
// DivisionPrecision digits after the decimal point.
|
|
func (d Decimal) Div(d2 Decimal) Decimal {
|
|
return d.DivRound(d2, int32(DivisionPrecision))
|
|
}
|
|
|
|
// QuoRem does divsion with remainder
|
|
// d.QuoRem(d2,precision) returns quotient q and remainder r such that
|
|
// d = d2 * q + r, q an integer multiple of 10^(-precision)
|
|
// 0 <= r < abs(d2) * 10 ^(-precision) if d>=0
|
|
// 0 >= r > -abs(d2) * 10 ^(-precision) if d<0
|
|
// Note that precision<0 is allowed as input.
|
|
func (d Decimal) QuoRem(d2 Decimal, precision int32) (Decimal, Decimal) {
|
|
d.ensureInitialized()
|
|
d2.ensureInitialized()
|
|
if d2.value.Sign() == 0 {
|
|
panic("decimal division by 0")
|
|
}
|
|
scale := -precision
|
|
e := int64(d.exp - d2.exp - scale)
|
|
if e > math.MaxInt32 || e < math.MinInt32 {
|
|
panic("overflow in decimal QuoRem")
|
|
}
|
|
var aa, bb, expo big.Int
|
|
var scalerest int32
|
|
// d = a 10^ea
|
|
// d2 = b 10^eb
|
|
if e < 0 {
|
|
aa = *d.value
|
|
expo.SetInt64(-e)
|
|
bb.Exp(tenInt, &expo, nil)
|
|
bb.Mul(d2.value, &bb)
|
|
scalerest = d.exp
|
|
// now aa = a
|
|
// bb = b 10^(scale + eb - ea)
|
|
} else {
|
|
expo.SetInt64(e)
|
|
aa.Exp(tenInt, &expo, nil)
|
|
aa.Mul(d.value, &aa)
|
|
bb = *d2.value
|
|
scalerest = scale + d2.exp
|
|
// now aa = a ^ (ea - eb - scale)
|
|
// bb = b
|
|
}
|
|
var q, r big.Int
|
|
q.QuoRem(&aa, &bb, &r)
|
|
dq := Decimal{value: &q, exp: scale}
|
|
dr := Decimal{value: &r, exp: scalerest}
|
|
return dq, dr
|
|
}
|
|
|
|
// DivRound divides and rounds to a given precision
|
|
// i.e. to an integer multiple of 10^(-precision)
|
|
// for a positive quotient digit 5 is rounded up, away from 0
|
|
// if the quotient is negative then digit 5 is rounded down, away from 0
|
|
// Note that precision<0 is allowed as input.
|
|
func (d Decimal) DivRound(d2 Decimal, precision int32) Decimal {
|
|
// QuoRem already checks initialization
|
|
q, r := d.QuoRem(d2, precision)
|
|
// the actual rounding decision is based on comparing r*10^precision and d2/2
|
|
// instead compare 2 r 10 ^precision and d2
|
|
var rv2 big.Int
|
|
rv2.Abs(r.value)
|
|
rv2.Lsh(&rv2, 1)
|
|
// now rv2 = abs(r.value) * 2
|
|
r2 := Decimal{value: &rv2, exp: r.exp + precision}
|
|
// r2 is now 2 * r * 10 ^ precision
|
|
var c = r2.Cmp(d2.Abs())
|
|
|
|
if c < 0 {
|
|
return q
|
|
}
|
|
|
|
if d.value.Sign()*d2.value.Sign() < 0 {
|
|
return q.Sub(New(1, -precision))
|
|
}
|
|
|
|
return q.Add(New(1, -precision))
|
|
}
|
|
|
|
// Mod returns d % d2.
|
|
func (d Decimal) Mod(d2 Decimal) Decimal {
|
|
quo := d.Div(d2).Truncate(0)
|
|
return d.Sub(d2.Mul(quo))
|
|
}
|
|
|
|
// Pow returns d to the power d2
|
|
func (d Decimal) Pow(d2 Decimal) Decimal {
|
|
var temp Decimal
|
|
if d2.IntPart() == 0 {
|
|
return NewFromFloat(1)
|
|
}
|
|
temp = d.Pow(d2.Div(NewFromFloat(2)))
|
|
if d2.IntPart()%2 == 0 {
|
|
return temp.Mul(temp)
|
|
}
|
|
if d2.IntPart() > 0 {
|
|
return temp.Mul(temp).Mul(d)
|
|
}
|
|
return temp.Mul(temp).Div(d)
|
|
}
|
|
|
|
// Cmp compares the numbers represented by d and d2 and returns:
|
|
//
|
|
// -1 if d < d2
|
|
// 0 if d == d2
|
|
// +1 if d > d2
|
|
//
|
|
func (d Decimal) Cmp(d2 Decimal) int {
|
|
d.ensureInitialized()
|
|
d2.ensureInitialized()
|
|
|
|
if d.exp == d2.exp {
|
|
return d.value.Cmp(d2.value)
|
|
}
|
|
|
|
rd, rd2 := RescalePair(d, d2)
|
|
|
|
return rd.value.Cmp(rd2.value)
|
|
}
|
|
|
|
// Equal returns whether the numbers represented by d and d2 are equal.
|
|
func (d Decimal) Equal(d2 Decimal) bool {
|
|
return d.Cmp(d2) == 0
|
|
}
|
|
|
|
// Equals is deprecated, please use Equal method instead
|
|
func (d Decimal) Equals(d2 Decimal) bool {
|
|
return d.Equal(d2)
|
|
}
|
|
|
|
// GreaterThan (GT) returns true when d is greater than d2.
|
|
func (d Decimal) GreaterThan(d2 Decimal) bool {
|
|
return d.Cmp(d2) == 1
|
|
}
|
|
|
|
// GreaterThanOrEqual (GTE) returns true when d is greater than or equal to d2.
|
|
func (d Decimal) GreaterThanOrEqual(d2 Decimal) bool {
|
|
cmp := d.Cmp(d2)
|
|
return cmp == 1 || cmp == 0
|
|
}
|
|
|
|
// LessThan (LT) returns true when d is less than d2.
|
|
func (d Decimal) LessThan(d2 Decimal) bool {
|
|
return d.Cmp(d2) == -1
|
|
}
|
|
|
|
// LessThanOrEqual (LTE) returns true when d is less than or equal to d2.
|
|
func (d Decimal) LessThanOrEqual(d2 Decimal) bool {
|
|
cmp := d.Cmp(d2)
|
|
return cmp == -1 || cmp == 0
|
|
}
|
|
|
|
// Sign returns:
|
|
//
|
|
// -1 if d < 0
|
|
// 0 if d == 0
|
|
// +1 if d > 0
|
|
//
|
|
func (d Decimal) Sign() int {
|
|
if d.value == nil {
|
|
return 0
|
|
}
|
|
return d.value.Sign()
|
|
}
|
|
|
|
// IsPositive return
|
|
//
|
|
// true if d > 0
|
|
// false if d == 0
|
|
// false if d < 0
|
|
func (d Decimal) IsPositive() bool {
|
|
return d.Sign() == 1
|
|
}
|
|
|
|
// IsNegative return
|
|
//
|
|
// true if d < 0
|
|
// false if d == 0
|
|
// false if d > 0
|
|
func (d Decimal) IsNegative() bool {
|
|
return d.Sign() == -1
|
|
}
|
|
|
|
// IsZero return
|
|
//
|
|
// true if d == 0
|
|
// false if d > 0
|
|
// false if d < 0
|
|
func (d Decimal) IsZero() bool {
|
|
return d.Sign() == 0
|
|
}
|
|
|
|
// Exponent returns the exponent, or scale component of the decimal.
|
|
func (d Decimal) Exponent() int32 {
|
|
return d.exp
|
|
}
|
|
|
|
// Coefficient returns the coefficient of the decimal. It is scaled by 10^Exponent()
|
|
func (d Decimal) Coefficient() *big.Int {
|
|
d.ensureInitialized()
|
|
// we copy the coefficient so that mutating the result does not mutate the
|
|
// Decimal.
|
|
return big.NewInt(0).Set(d.value)
|
|
}
|
|
|
|
// IntPart returns the integer component of the decimal.
|
|
func (d Decimal) IntPart() int64 {
|
|
scaledD := d.rescale(0)
|
|
return scaledD.value.Int64()
|
|
}
|
|
|
|
// BigInt returns integer component of the decimal as a BigInt.
|
|
func (d Decimal) BigInt() *big.Int {
|
|
scaledD := d.rescale(0)
|
|
i := &big.Int{}
|
|
i.SetString(scaledD.String(), 10)
|
|
return i
|
|
}
|
|
|
|
// BigFloat returns decimal as BigFloat.
|
|
// Be aware that casting decimal to BigFloat might cause a loss of precision.
|
|
func (d Decimal) BigFloat() *big.Float {
|
|
f := &big.Float{}
|
|
f.SetString(d.String())
|
|
return f
|
|
}
|
|
|
|
// Rat returns a rational number representation of the decimal.
|
|
func (d Decimal) Rat() *big.Rat {
|
|
d.ensureInitialized()
|
|
if d.exp <= 0 {
|
|
// NOTE(vadim): must negate after casting to prevent int32 overflow
|
|
denom := new(big.Int).Exp(tenInt, big.NewInt(-int64(d.exp)), nil)
|
|
return new(big.Rat).SetFrac(d.value, denom)
|
|
}
|
|
|
|
mul := new(big.Int).Exp(tenInt, big.NewInt(int64(d.exp)), nil)
|
|
num := new(big.Int).Mul(d.value, mul)
|
|
return new(big.Rat).SetFrac(num, oneInt)
|
|
}
|
|
|
|
// Float64 returns the nearest float64 value for d and a bool indicating
|
|
// whether f represents d exactly.
|
|
// For more details, see the documentation for big.Rat.Float64
|
|
func (d Decimal) Float64() (f float64, exact bool) {
|
|
return d.Rat().Float64()
|
|
}
|
|
|
|
// String returns the string representation of the decimal
|
|
// with the fixed point.
|
|
//
|
|
// Example:
|
|
//
|
|
// d := New(-12345, -3)
|
|
// println(d.String())
|
|
//
|
|
// Output:
|
|
//
|
|
// -12.345
|
|
//
|
|
func (d Decimal) String() string {
|
|
return d.string(true)
|
|
}
|
|
|
|
// StringFixed returns a rounded fixed-point string with places digits after
|
|
// the decimal point.
|
|
//
|
|
// Example:
|
|
//
|
|
// NewFromFloat(0).StringFixed(2) // output: "0.00"
|
|
// NewFromFloat(0).StringFixed(0) // output: "0"
|
|
// NewFromFloat(5.45).StringFixed(0) // output: "5"
|
|
// NewFromFloat(5.45).StringFixed(1) // output: "5.5"
|
|
// NewFromFloat(5.45).StringFixed(2) // output: "5.45"
|
|
// NewFromFloat(5.45).StringFixed(3) // output: "5.450"
|
|
// NewFromFloat(545).StringFixed(-1) // output: "550"
|
|
//
|
|
func (d Decimal) StringFixed(places int32) string {
|
|
rounded := d.Round(places)
|
|
return rounded.string(false)
|
|
}
|
|
|
|
// StringFixedBank returns a banker rounded fixed-point string with places digits
|
|
// after the decimal point.
|
|
//
|
|
// Example:
|
|
//
|
|
// NewFromFloat(0).StringFixedBank(2) // output: "0.00"
|
|
// NewFromFloat(0).StringFixedBank(0) // output: "0"
|
|
// NewFromFloat(5.45).StringFixedBank(0) // output: "5"
|
|
// NewFromFloat(5.45).StringFixedBank(1) // output: "5.4"
|
|
// NewFromFloat(5.45).StringFixedBank(2) // output: "5.45"
|
|
// NewFromFloat(5.45).StringFixedBank(3) // output: "5.450"
|
|
// NewFromFloat(545).StringFixedBank(-1) // output: "540"
|
|
//
|
|
func (d Decimal) StringFixedBank(places int32) string {
|
|
rounded := d.RoundBank(places)
|
|
return rounded.string(false)
|
|
}
|
|
|
|
// StringFixedCash returns a Swedish/Cash rounded fixed-point string. For
|
|
// more details see the documentation at function RoundCash.
|
|
func (d Decimal) StringFixedCash(interval uint8) string {
|
|
rounded := d.RoundCash(interval)
|
|
return rounded.string(false)
|
|
}
|
|
|
|
// Round rounds the decimal to places decimal places.
|
|
// If places < 0, it will round the integer part to the nearest 10^(-places).
|
|
//
|
|
// Example:
|
|
//
|
|
// NewFromFloat(5.45).Round(1).String() // output: "5.5"
|
|
// NewFromFloat(545).Round(-1).String() // output: "550"
|
|
//
|
|
func (d Decimal) Round(places int32) Decimal {
|
|
// truncate to places + 1
|
|
ret := d.rescale(-places - 1)
|
|
|
|
// add sign(d) * 0.5
|
|
if ret.value.Sign() < 0 {
|
|
ret.value.Sub(ret.value, fiveInt)
|
|
} else {
|
|
ret.value.Add(ret.value, fiveInt)
|
|
}
|
|
|
|
// floor for positive numbers, ceil for negative numbers
|
|
_, m := ret.value.DivMod(ret.value, tenInt, new(big.Int))
|
|
ret.exp++
|
|
if ret.value.Sign() < 0 && m.Cmp(zeroInt) != 0 {
|
|
ret.value.Add(ret.value, oneInt)
|
|
}
|
|
|
|
return ret
|
|
}
|
|
|
|
// RoundBank rounds the decimal to places decimal places.
|
|
// If the final digit to round is equidistant from the nearest two integers the
|
|
// rounded value is taken as the even number
|
|
//
|
|
// If places < 0, it will round the integer part to the nearest 10^(-places).
|
|
//
|
|
// Examples:
|
|
//
|
|
// NewFromFloat(5.45).Round(1).String() // output: "5.4"
|
|
// NewFromFloat(545).Round(-1).String() // output: "540"
|
|
// NewFromFloat(5.46).Round(1).String() // output: "5.5"
|
|
// NewFromFloat(546).Round(-1).String() // output: "550"
|
|
// NewFromFloat(5.55).Round(1).String() // output: "5.6"
|
|
// NewFromFloat(555).Round(-1).String() // output: "560"
|
|
//
|
|
func (d Decimal) RoundBank(places int32) Decimal {
|
|
|
|
round := d.Round(places)
|
|
remainder := d.Sub(round).Abs()
|
|
|
|
half := New(5, -places-1)
|
|
if remainder.Cmp(half) == 0 && round.value.Bit(0) != 0 {
|
|
if round.value.Sign() < 0 {
|
|
round.value.Add(round.value, oneInt)
|
|
} else {
|
|
round.value.Sub(round.value, oneInt)
|
|
}
|
|
}
|
|
|
|
return round
|
|
}
|
|
|
|
// RoundCash aka Cash/Penny/öre rounding rounds decimal to a specific
|
|
// interval. The amount payable for a cash transaction is rounded to the nearest
|
|
// multiple of the minimum currency unit available. The following intervals are
|
|
// available: 5, 10, 25, 50 and 100; any other number throws a panic.
|
|
// 5: 5 cent rounding 3.43 => 3.45
|
|
// 10: 10 cent rounding 3.45 => 3.50 (5 gets rounded up)
|
|
// 25: 25 cent rounding 3.41 => 3.50
|
|
// 50: 50 cent rounding 3.75 => 4.00
|
|
// 100: 100 cent rounding 3.50 => 4.00
|
|
// For more details: https://en.wikipedia.org/wiki/Cash_rounding
|
|
func (d Decimal) RoundCash(interval uint8) Decimal {
|
|
var iVal *big.Int
|
|
switch interval {
|
|
case 5:
|
|
iVal = twentyInt
|
|
case 10:
|
|
iVal = tenInt
|
|
case 25:
|
|
iVal = fourInt
|
|
case 50:
|
|
iVal = twoInt
|
|
case 100:
|
|
iVal = oneInt
|
|
default:
|
|
panic(fmt.Sprintf("Decimal does not support this Cash rounding interval `%d`. Supported: 5, 10, 25, 50, 100", interval))
|
|
}
|
|
dVal := Decimal{
|
|
value: iVal,
|
|
}
|
|
|
|
// TODO: optimize those calculations to reduce the high allocations (~29 allocs).
|
|
return d.Mul(dVal).Round(0).Div(dVal).Truncate(2)
|
|
}
|
|
|
|
// Floor returns the nearest integer value less than or equal to d.
|
|
func (d Decimal) Floor() Decimal {
|
|
d.ensureInitialized()
|
|
|
|
if d.exp >= 0 {
|
|
return d
|
|
}
|
|
|
|
exp := big.NewInt(10)
|
|
|
|
// NOTE(vadim): must negate after casting to prevent int32 overflow
|
|
exp.Exp(exp, big.NewInt(-int64(d.exp)), nil)
|
|
|
|
z := new(big.Int).Div(d.value, exp)
|
|
return Decimal{value: z, exp: 0}
|
|
}
|
|
|
|
// Ceil returns the nearest integer value greater than or equal to d.
|
|
func (d Decimal) Ceil() Decimal {
|
|
d.ensureInitialized()
|
|
|
|
if d.exp >= 0 {
|
|
return d
|
|
}
|
|
|
|
exp := big.NewInt(10)
|
|
|
|
// NOTE(vadim): must negate after casting to prevent int32 overflow
|
|
exp.Exp(exp, big.NewInt(-int64(d.exp)), nil)
|
|
|
|
z, m := new(big.Int).DivMod(d.value, exp, new(big.Int))
|
|
if m.Cmp(zeroInt) != 0 {
|
|
z.Add(z, oneInt)
|
|
}
|
|
return Decimal{value: z, exp: 0}
|
|
}
|
|
|
|
// Truncate truncates off digits from the number, without rounding.
|
|
//
|
|
// NOTE: precision is the last digit that will not be truncated (must be >= 0).
|
|
//
|
|
// Example:
|
|
//
|
|
// decimal.NewFromString("123.456").Truncate(2).String() // "123.45"
|
|
//
|
|
func (d Decimal) Truncate(precision int32) Decimal {
|
|
d.ensureInitialized()
|
|
if precision >= 0 && -precision > d.exp {
|
|
return d.rescale(-precision)
|
|
}
|
|
return d
|
|
}
|
|
|
|
// UnmarshalJSON implements the json.Unmarshaler interface.
|
|
func (d *Decimal) UnmarshalJSON(decimalBytes []byte) error {
|
|
if string(decimalBytes) == "null" {
|
|
return nil
|
|
}
|
|
|
|
str, err := unquoteIfQuoted(decimalBytes)
|
|
if err != nil {
|
|
return fmt.Errorf("error decoding string '%s': %s", decimalBytes, err)
|
|
}
|
|
|
|
decimal, err := NewFromString(str)
|
|
*d = decimal
|
|
if err != nil {
|
|
return fmt.Errorf("error decoding string '%s': %s", str, err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// MarshalJSON implements the json.Marshaler interface.
|
|
func (d Decimal) MarshalJSON() ([]byte, error) {
|
|
var str string
|
|
if MarshalJSONWithoutQuotes {
|
|
str = d.String()
|
|
} else {
|
|
str = "\"" + d.String() + "\""
|
|
}
|
|
return []byte(str), nil
|
|
}
|
|
|
|
// UnmarshalBinary implements the encoding.BinaryUnmarshaler interface. As a string representation
|
|
// is already used when encoding to text, this method stores that string as []byte
|
|
func (d *Decimal) UnmarshalBinary(data []byte) error {
|
|
// Extract the exponent
|
|
d.exp = int32(binary.BigEndian.Uint32(data[:4]))
|
|
|
|
// Extract the value
|
|
d.value = new(big.Int)
|
|
return d.value.GobDecode(data[4:])
|
|
}
|
|
|
|
// MarshalBinary implements the encoding.BinaryMarshaler interface.
|
|
func (d Decimal) MarshalBinary() (data []byte, err error) {
|
|
// Write the exponent first since it's a fixed size
|
|
v1 := make([]byte, 4)
|
|
binary.BigEndian.PutUint32(v1, uint32(d.exp))
|
|
|
|
// Add the value
|
|
var v2 []byte
|
|
if v2, err = d.value.GobEncode(); err != nil {
|
|
return
|
|
}
|
|
|
|
// Return the byte array
|
|
data = append(v1, v2...)
|
|
return
|
|
}
|
|
|
|
// Scan implements the sql.Scanner interface for database deserialization.
|
|
func (d *Decimal) Scan(value interface{}) error {
|
|
// first try to see if the data is stored in database as a Numeric datatype
|
|
switch v := value.(type) {
|
|
|
|
case float32:
|
|
*d = NewFromFloat(float64(v))
|
|
return nil
|
|
|
|
case float64:
|
|
// numeric in sqlite3 sends us float64
|
|
*d = NewFromFloat(v)
|
|
return nil
|
|
|
|
case int64:
|
|
// at least in sqlite3 when the value is 0 in db, the data is sent
|
|
// to us as an int64 instead of a float64 ...
|
|
*d = New(v, 0)
|
|
return nil
|
|
|
|
default:
|
|
// default is trying to interpret value stored as string
|
|
str, err := unquoteIfQuoted(v)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
*d, err = NewFromString(str)
|
|
return err
|
|
}
|
|
}
|
|
|
|
// Value implements the driver.Valuer interface for database serialization.
|
|
func (d Decimal) Value() (driver.Value, error) {
|
|
return d.String(), nil
|
|
}
|
|
|
|
// UnmarshalText implements the encoding.TextUnmarshaler interface for XML
|
|
// deserialization.
|
|
func (d *Decimal) UnmarshalText(text []byte) error {
|
|
str := string(text)
|
|
|
|
dec, err := NewFromString(str)
|
|
*d = dec
|
|
if err != nil {
|
|
return fmt.Errorf("error decoding string '%s': %s", str, err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// MarshalText implements the encoding.TextMarshaler interface for XML
|
|
// serialization.
|
|
func (d Decimal) MarshalText() (text []byte, err error) {
|
|
return []byte(d.String()), nil
|
|
}
|
|
|
|
// GobEncode implements the gob.GobEncoder interface for gob serialization.
|
|
func (d Decimal) GobEncode() ([]byte, error) {
|
|
return d.MarshalBinary()
|
|
}
|
|
|
|
// GobDecode implements the gob.GobDecoder interface for gob serialization.
|
|
func (d *Decimal) GobDecode(data []byte) error {
|
|
return d.UnmarshalBinary(data)
|
|
}
|
|
|
|
// StringScaled first scales the decimal then calls .String() on it.
|
|
// NOTE: buggy, unintuitive, and DEPRECATED! Use StringFixed instead.
|
|
func (d Decimal) StringScaled(exp int32) string {
|
|
return d.rescale(exp).String()
|
|
}
|
|
|
|
func (d Decimal) string(trimTrailingZeros bool) string {
|
|
if d.exp >= 0 {
|
|
return d.rescale(0).value.String()
|
|
}
|
|
|
|
abs := new(big.Int).Abs(d.value)
|
|
str := abs.String()
|
|
|
|
var intPart, fractionalPart string
|
|
|
|
// NOTE(vadim): this cast to int will cause bugs if d.exp == INT_MIN
|
|
// and you are on a 32-bit machine. Won't fix this super-edge case.
|
|
dExpInt := int(d.exp)
|
|
if len(str) > -dExpInt {
|
|
intPart = str[:len(str)+dExpInt]
|
|
fractionalPart = str[len(str)+dExpInt:]
|
|
} else {
|
|
intPart = "0"
|
|
|
|
num0s := -dExpInt - len(str)
|
|
fractionalPart = strings.Repeat("0", num0s) + str
|
|
}
|
|
|
|
if trimTrailingZeros {
|
|
i := len(fractionalPart) - 1
|
|
for ; i >= 0; i-- {
|
|
if fractionalPart[i] != '0' {
|
|
break
|
|
}
|
|
}
|
|
fractionalPart = fractionalPart[:i+1]
|
|
}
|
|
|
|
number := intPart
|
|
if len(fractionalPart) > 0 {
|
|
number += "." + fractionalPart
|
|
}
|
|
|
|
if d.value.Sign() < 0 {
|
|
return "-" + number
|
|
}
|
|
|
|
return number
|
|
}
|
|
|
|
func (d *Decimal) ensureInitialized() {
|
|
if d.value == nil {
|
|
d.value = new(big.Int)
|
|
}
|
|
}
|
|
|
|
// Min returns the smallest Decimal that was passed in the arguments.
|
|
//
|
|
// To call this function with an array, you must do:
|
|
//
|
|
// Min(arr[0], arr[1:]...)
|
|
//
|
|
// This makes it harder to accidentally call Min with 0 arguments.
|
|
func Min(first Decimal, rest ...Decimal) Decimal {
|
|
ans := first
|
|
for _, item := range rest {
|
|
if item.Cmp(ans) < 0 {
|
|
ans = item
|
|
}
|
|
}
|
|
return ans
|
|
}
|
|
|
|
// Max returns the largest Decimal that was passed in the arguments.
|
|
//
|
|
// To call this function with an array, you must do:
|
|
//
|
|
// Max(arr[0], arr[1:]...)
|
|
//
|
|
// This makes it harder to accidentally call Max with 0 arguments.
|
|
func Max(first Decimal, rest ...Decimal) Decimal {
|
|
ans := first
|
|
for _, item := range rest {
|
|
if item.Cmp(ans) > 0 {
|
|
ans = item
|
|
}
|
|
}
|
|
return ans
|
|
}
|
|
|
|
// Sum returns the combined total of the provided first and rest Decimals
|
|
func Sum(first Decimal, rest ...Decimal) Decimal {
|
|
total := first
|
|
for _, item := range rest {
|
|
total = total.Add(item)
|
|
}
|
|
|
|
return total
|
|
}
|
|
|
|
// Avg returns the average value of the provided first and rest Decimals
|
|
func Avg(first Decimal, rest ...Decimal) Decimal {
|
|
count := New(int64(len(rest)+1), 0)
|
|
sum := Sum(first, rest...)
|
|
return sum.Div(count)
|
|
}
|
|
|
|
// RescalePair rescales two decimals to common exponential value (minimal exp of both decimals)
|
|
func RescalePair(d1 Decimal, d2 Decimal) (Decimal, Decimal) {
|
|
d1.ensureInitialized()
|
|
d2.ensureInitialized()
|
|
|
|
if d1.exp == d2.exp {
|
|
return d1, d2
|
|
}
|
|
|
|
baseScale := min(d1.exp, d2.exp)
|
|
if baseScale != d1.exp {
|
|
return d1.rescale(baseScale), d2
|
|
}
|
|
return d1, d2.rescale(baseScale)
|
|
}
|
|
|
|
func min(x, y int32) int32 {
|
|
if x >= y {
|
|
return y
|
|
}
|
|
return x
|
|
}
|
|
|
|
func unquoteIfQuoted(value interface{}) (string, error) {
|
|
var bytes []byte
|
|
|
|
switch v := value.(type) {
|
|
case string:
|
|
bytes = []byte(v)
|
|
case []byte:
|
|
bytes = v
|
|
default:
|
|
return "", fmt.Errorf("could not convert value '%+v' to byte array of type '%T'",
|
|
value, value)
|
|
}
|
|
|
|
// If the amount is quoted, strip the quotes
|
|
if len(bytes) > 2 && bytes[0] == '"' && bytes[len(bytes)-1] == '"' {
|
|
bytes = bytes[1 : len(bytes)-1]
|
|
}
|
|
return string(bytes), nil
|
|
}
|
|
|
|
// NullDecimal represents a nullable decimal with compatibility for
|
|
// scanning null values from the database.
|
|
type NullDecimal struct {
|
|
Decimal Decimal
|
|
Valid bool
|
|
}
|
|
|
|
// Scan implements the sql.Scanner interface for database deserialization.
|
|
func (d *NullDecimal) Scan(value interface{}) error {
|
|
if value == nil {
|
|
d.Valid = false
|
|
return nil
|
|
}
|
|
d.Valid = true
|
|
return d.Decimal.Scan(value)
|
|
}
|
|
|
|
// Value implements the driver.Valuer interface for database serialization.
|
|
func (d NullDecimal) Value() (driver.Value, error) {
|
|
if !d.Valid {
|
|
return nil, nil
|
|
}
|
|
return d.Decimal.Value()
|
|
}
|
|
|
|
// UnmarshalJSON implements the json.Unmarshaler interface.
|
|
func (d *NullDecimal) UnmarshalJSON(decimalBytes []byte) error {
|
|
if string(decimalBytes) == "null" {
|
|
d.Valid = false
|
|
return nil
|
|
}
|
|
d.Valid = true
|
|
return d.Decimal.UnmarshalJSON(decimalBytes)
|
|
}
|
|
|
|
// MarshalJSON implements the json.Marshaler interface.
|
|
func (d NullDecimal) MarshalJSON() ([]byte, error) {
|
|
if !d.Valid {
|
|
return []byte("null"), nil
|
|
}
|
|
return d.Decimal.MarshalJSON()
|
|
}
|
|
|
|
// Trig functions
|
|
|
|
// Atan returns the arctangent, in radians, of x.
|
|
func (d Decimal) Atan() Decimal {
|
|
if d.Equal(NewFromFloat(0.0)) {
|
|
return d
|
|
}
|
|
if d.GreaterThan(NewFromFloat(0.0)) {
|
|
return d.satan()
|
|
}
|
|
return d.Neg().satan().Neg()
|
|
}
|
|
|
|
func (d Decimal) xatan() Decimal {
|
|
P0 := NewFromFloat(-8.750608600031904122785e-01)
|
|
P1 := NewFromFloat(-1.615753718733365076637e+01)
|
|
P2 := NewFromFloat(-7.500855792314704667340e+01)
|
|
P3 := NewFromFloat(-1.228866684490136173410e+02)
|
|
P4 := NewFromFloat(-6.485021904942025371773e+01)
|
|
Q0 := NewFromFloat(2.485846490142306297962e+01)
|
|
Q1 := NewFromFloat(1.650270098316988542046e+02)
|
|
Q2 := NewFromFloat(4.328810604912902668951e+02)
|
|
Q3 := NewFromFloat(4.853903996359136964868e+02)
|
|
Q4 := NewFromFloat(1.945506571482613964425e+02)
|
|
z := d.Mul(d)
|
|
b1 := P0.Mul(z).Add(P1).Mul(z).Add(P2).Mul(z).Add(P3).Mul(z).Add(P4).Mul(z)
|
|
b2 := z.Add(Q0).Mul(z).Add(Q1).Mul(z).Add(Q2).Mul(z).Add(Q3).Mul(z).Add(Q4)
|
|
z = b1.Div(b2)
|
|
z = d.Mul(z).Add(d)
|
|
return z
|
|
}
|
|
|
|
// satan reduces its argument (known to be positive)
|
|
// to the range [0, 0.66] and calls xatan.
|
|
func (d Decimal) satan() Decimal {
|
|
Morebits := NewFromFloat(6.123233995736765886130e-17) // pi/2 = PIO2 + Morebits
|
|
Tan3pio8 := NewFromFloat(2.41421356237309504880) // tan(3*pi/8)
|
|
pi := NewFromFloat(3.14159265358979323846264338327950288419716939937510582097494459)
|
|
|
|
if d.LessThanOrEqual(NewFromFloat(0.66)) {
|
|
return d.xatan()
|
|
}
|
|
if d.GreaterThan(Tan3pio8) {
|
|
return pi.Div(NewFromFloat(2.0)).Sub(NewFromFloat(1.0).Div(d).xatan()).Add(Morebits)
|
|
}
|
|
return pi.Div(NewFromFloat(4.0)).Add((d.Sub(NewFromFloat(1.0)).Div(d.Add(NewFromFloat(1.0)))).xatan()).Add(NewFromFloat(0.5).Mul(Morebits))
|
|
}
|
|
|
|
// sin coefficients
|
|
var _sin = [...]Decimal{
|
|
NewFromFloat(1.58962301576546568060e-10), // 0x3de5d8fd1fd19ccd
|
|
NewFromFloat(-2.50507477628578072866e-8), // 0xbe5ae5e5a9291f5d
|
|
NewFromFloat(2.75573136213857245213e-6), // 0x3ec71de3567d48a1
|
|
NewFromFloat(-1.98412698295895385996e-4), // 0xbf2a01a019bfdf03
|
|
NewFromFloat(8.33333333332211858878e-3), // 0x3f8111111110f7d0
|
|
NewFromFloat(-1.66666666666666307295e-1), // 0xbfc5555555555548
|
|
}
|
|
|
|
// Sin returns the sine of the radian argument x.
|
|
func (d Decimal) Sin() Decimal {
|
|
PI4A := NewFromFloat(7.85398125648498535156e-1) // 0x3fe921fb40000000, Pi/4 split into three parts
|
|
PI4B := NewFromFloat(3.77489470793079817668e-8) // 0x3e64442d00000000,
|
|
PI4C := NewFromFloat(2.69515142907905952645e-15) // 0x3ce8469898cc5170,
|
|
M4PI := NewFromFloat(1.273239544735162542821171882678754627704620361328125) // 4/pi
|
|
|
|
if d.Equal(NewFromFloat(0.0)) {
|
|
return d
|
|
}
|
|
// make argument positive but save the sign
|
|
sign := false
|
|
if d.LessThan(NewFromFloat(0.0)) {
|
|
d = d.Neg()
|
|
sign = true
|
|
}
|
|
|
|
j := d.Mul(M4PI).IntPart() // integer part of x/(Pi/4), as integer for tests on the phase angle
|
|
y := NewFromFloat(float64(j)) // integer part of x/(Pi/4), as float
|
|
|
|
// map zeros to origin
|
|
if j&1 == 1 {
|
|
j++
|
|
y = y.Add(NewFromFloat(1.0))
|
|
}
|
|
j &= 7 // octant modulo 2Pi radians (360 degrees)
|
|
// reflect in x axis
|
|
if j > 3 {
|
|
sign = !sign
|
|
j -= 4
|
|
}
|
|
z := d.Sub(y.Mul(PI4A)).Sub(y.Mul(PI4B)).Sub(y.Mul(PI4C)) // Extended precision modular arithmetic
|
|
zz := z.Mul(z)
|
|
|
|
if j == 1 || j == 2 {
|
|
w := zz.Mul(zz).Mul(_cos[0].Mul(zz).Add(_cos[1]).Mul(zz).Add(_cos[2]).Mul(zz).Add(_cos[3]).Mul(zz).Add(_cos[4]).Mul(zz).Add(_cos[5]))
|
|
y = NewFromFloat(1.0).Sub(NewFromFloat(0.5).Mul(zz)).Add(w)
|
|
} else {
|
|
y = z.Add(z.Mul(zz).Mul(_sin[0].Mul(zz).Add(_sin[1]).Mul(zz).Add(_sin[2]).Mul(zz).Add(_sin[3]).Mul(zz).Add(_sin[4]).Mul(zz).Add(_sin[5])))
|
|
}
|
|
if sign {
|
|
y = y.Neg()
|
|
}
|
|
return y
|
|
}
|
|
|
|
// cos coefficients
|
|
var _cos = [...]Decimal{
|
|
NewFromFloat(-1.13585365213876817300e-11), // 0xbda8fa49a0861a9b
|
|
NewFromFloat(2.08757008419747316778e-9), // 0x3e21ee9d7b4e3f05
|
|
NewFromFloat(-2.75573141792967388112e-7), // 0xbe927e4f7eac4bc6
|
|
NewFromFloat(2.48015872888517045348e-5), // 0x3efa01a019c844f5
|
|
NewFromFloat(-1.38888888888730564116e-3), // 0xbf56c16c16c14f91
|
|
NewFromFloat(4.16666666666665929218e-2), // 0x3fa555555555554b
|
|
}
|
|
|
|
// Cos returns the cosine of the radian argument x.
|
|
func (d Decimal) Cos() Decimal {
|
|
|
|
PI4A := NewFromFloat(7.85398125648498535156e-1) // 0x3fe921fb40000000, Pi/4 split into three parts
|
|
PI4B := NewFromFloat(3.77489470793079817668e-8) // 0x3e64442d00000000,
|
|
PI4C := NewFromFloat(2.69515142907905952645e-15) // 0x3ce8469898cc5170,
|
|
M4PI := NewFromFloat(1.273239544735162542821171882678754627704620361328125) // 4/pi
|
|
|
|
// make argument positive
|
|
sign := false
|
|
if d.LessThan(NewFromFloat(0.0)) {
|
|
d = d.Neg()
|
|
}
|
|
|
|
j := d.Mul(M4PI).IntPart() // integer part of x/(Pi/4), as integer for tests on the phase angle
|
|
y := NewFromFloat(float64(j)) // integer part of x/(Pi/4), as float
|
|
|
|
// map zeros to origin
|
|
if j&1 == 1 {
|
|
j++
|
|
y = y.Add(NewFromFloat(1.0))
|
|
}
|
|
j &= 7 // octant modulo 2Pi radians (360 degrees)
|
|
// reflect in x axis
|
|
if j > 3 {
|
|
sign = !sign
|
|
j -= 4
|
|
}
|
|
if j > 1 {
|
|
sign = !sign
|
|
}
|
|
|
|
z := d.Sub(y.Mul(PI4A)).Sub(y.Mul(PI4B)).Sub(y.Mul(PI4C)) // Extended precision modular arithmetic
|
|
zz := z.Mul(z)
|
|
|
|
if j == 1 || j == 2 {
|
|
y = z.Add(z.Mul(zz).Mul(_sin[0].Mul(zz).Add(_sin[1]).Mul(zz).Add(_sin[2]).Mul(zz).Add(_sin[3]).Mul(zz).Add(_sin[4]).Mul(zz).Add(_sin[5])))
|
|
} else {
|
|
w := zz.Mul(zz).Mul(_cos[0].Mul(zz).Add(_cos[1]).Mul(zz).Add(_cos[2]).Mul(zz).Add(_cos[3]).Mul(zz).Add(_cos[4]).Mul(zz).Add(_cos[5]))
|
|
y = NewFromFloat(1.0).Sub(NewFromFloat(0.5).Mul(zz)).Add(w)
|
|
}
|
|
if sign {
|
|
y = y.Neg()
|
|
}
|
|
return y
|
|
}
|
|
|
|
var _tanP = [...]Decimal{
|
|
NewFromFloat(-1.30936939181383777646e+4), // 0xc0c992d8d24f3f38
|
|
NewFromFloat(1.15351664838587416140e+6), // 0x413199eca5fc9ddd
|
|
NewFromFloat(-1.79565251976484877988e+7), // 0xc1711fead3299176
|
|
}
|
|
var _tanQ = [...]Decimal{
|
|
NewFromFloat(1.00000000000000000000e+0),
|
|
NewFromFloat(1.36812963470692954678e+4), //0x40cab8a5eeb36572
|
|
NewFromFloat(-1.32089234440210967447e+6), //0xc13427bc582abc96
|
|
NewFromFloat(2.50083801823357915839e+7), //0x4177d98fc2ead8ef
|
|
NewFromFloat(-5.38695755929454629881e+7), //0xc189afe03cbe5a31
|
|
}
|
|
|
|
// Tan returns the tangent of the radian argument x.
|
|
func (d Decimal) Tan() Decimal {
|
|
|
|
PI4A := NewFromFloat(7.85398125648498535156e-1) // 0x3fe921fb40000000, Pi/4 split into three parts
|
|
PI4B := NewFromFloat(3.77489470793079817668e-8) // 0x3e64442d00000000,
|
|
PI4C := NewFromFloat(2.69515142907905952645e-15) // 0x3ce8469898cc5170,
|
|
M4PI := NewFromFloat(1.273239544735162542821171882678754627704620361328125) // 4/pi
|
|
|
|
if d.Equal(NewFromFloat(0.0)) {
|
|
return d
|
|
}
|
|
|
|
// make argument positive but save the sign
|
|
sign := false
|
|
if d.LessThan(NewFromFloat(0.0)) {
|
|
d = d.Neg()
|
|
sign = true
|
|
}
|
|
|
|
j := d.Mul(M4PI).IntPart() // integer part of x/(Pi/4), as integer for tests on the phase angle
|
|
y := NewFromFloat(float64(j)) // integer part of x/(Pi/4), as float
|
|
|
|
// map zeros to origin
|
|
if j&1 == 1 {
|
|
j++
|
|
y = y.Add(NewFromFloat(1.0))
|
|
}
|
|
|
|
z := d.Sub(y.Mul(PI4A)).Sub(y.Mul(PI4B)).Sub(y.Mul(PI4C)) // Extended precision modular arithmetic
|
|
zz := z.Mul(z)
|
|
|
|
if zz.GreaterThan(NewFromFloat(1e-14)) {
|
|
w := zz.Mul(_tanP[0].Mul(zz).Add(_tanP[1]).Mul(zz).Add(_tanP[2]))
|
|
x := zz.Add(_tanQ[1]).Mul(zz).Add(_tanQ[2]).Mul(zz).Add(_tanQ[3]).Mul(zz).Add(_tanQ[4])
|
|
y = z.Add(z.Mul(w.Div(x)))
|
|
} else {
|
|
y = z
|
|
}
|
|
if j&2 == 2 {
|
|
y = NewFromFloat(-1.0).Div(y)
|
|
}
|
|
if sign {
|
|
y = y.Neg()
|
|
}
|
|
return y
|
|
}
|