aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authoryenlin.lai <yenlin.lai@cobinhood.com>2019-03-25 15:07:55 +0800
committeryenlinlai <38415072+yenlinlai@users.noreply.github.com>2019-03-26 17:10:28 +0800
commit06bfa5d7fe752784170b7362c0bcf490382babd9 (patch)
treeec555ae30a9185147b85a93a706e30a3f8fd6202
parent1ad8c576925475b0190232ae482ad3b7f0d4611d (diff)
downloaddexon-decimal-06bfa5d7fe752784170b7362c0bcf490382babd9.tar.gz
dexon-decimal-06bfa5d7fe752784170b7362c0bcf490382babd9.tar.zst
dexon-decimal-06bfa5d7fe752784170b7362c0bcf490382babd9.zip
misc: optimize with precomputed constants to reduce computation
NewFromFloat is much more expensive than New or decimal copy. Reduce number of NewFromFloat by precomputing constant variables or by using New instead. However, some PI related NewFromFloat are still not removed as they are not all at same precision. Force normalizing to a common PI will break tests.
-rw-r--r--decimal.go83
1 files changed, 46 insertions, 37 deletions
diff --git a/decimal.go b/decimal.go
index 9d341ff..763c485 100644
--- a/decimal.go
+++ b/decimal.go
@@ -52,11 +52,14 @@ var DivisionPrecision = 16
// silently lose precision.
var MarshalJSONWithoutQuotes = false
-// Zero constant, to make computations faster.
-var Zero = New(0, 1)
-
-// fiveDec used in Cash Rounding
-var fiveDec = New(5, 0)
+// Common decimal constants, to make computations faster.
+var (
+ Zero = New(0, 0)
+ One = New(1, 0)
+ Two = New(2, 0)
+ Five = New(5, 0)
+ Ten = New(1, 1)
+)
var zeroInt = big.NewInt(0)
var oneInt = big.NewInt(1)
@@ -544,9 +547,9 @@ func (d Decimal) Mod(d2 Decimal) Decimal {
func (d Decimal) Pow(d2 Decimal) Decimal {
var temp Decimal
if d2.IntPart() == 0 {
- return NewFromFloat(1)
+ return One
}
- temp = d.Pow(d2.Div(NewFromFloat(2)))
+ temp = d.Pow(d2.Div(Two))
if d2.IntPart()%2 == 0 {
return temp.Mul(temp)
}
@@ -833,7 +836,7 @@ func (d Decimal) RoundCash(interval uint8) Decimal {
dOne := New(10^-int64(orgExp), orgExp)
d2 := d
d2.exp = 0
- if d2.Mod(fiveDec).Equal(Zero) {
+ if d2.Mod(Five).IsZero() {
d2.exp = orgExp
d2 = d2.Sub(dOne)
d = d2
@@ -1215,29 +1218,35 @@ func (d NullDecimal) MarshalJSON() ([]byte, error) {
// Atan returns the arctangent, in radians, of d.
func (d Decimal) Atan() Decimal {
- if d.Equal(NewFromFloat(0.0)) {
+ if d.IsZero() {
return d
}
- if d.GreaterThan(NewFromFloat(0.0)) {
+ if d.IsPositive() {
return d.satan()
}
return d.Neg().satan().Neg()
}
+var _xatanP = [...]Decimal{
+ NewFromFloat(-8.750608600031904122785e-01),
+ NewFromFloat(-1.615753718733365076637e+01),
+ NewFromFloat(-7.500855792314704667340e+01),
+ NewFromFloat(-1.228866684490136173410e+02),
+ NewFromFloat(-6.485021904942025371773e+01),
+}
+
+var _xatanQ = [...]Decimal{
+ NewFromFloat(2.485846490142306297962e+01),
+ NewFromFloat(1.650270098316988542046e+02),
+ NewFromFloat(4.328810604912902668951e+02),
+ NewFromFloat(4.853903996359136964868e+02),
+ NewFromFloat(1.945506571482613964425e+02),
+}
+
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)
+ b1 := _xatanP[0].Mul(z).Add(_xatanP[1]).Mul(z).Add(_xatanP[2]).Mul(z).Add(_xatanP[3]).Mul(z).Add(_xatanP[4]).Mul(z)
+ b2 := z.Add(_xatanQ[0]).Mul(z).Add(_xatanQ[1]).Mul(z).Add(_xatanQ[2]).Mul(z).Add(_xatanQ[3]).Mul(z).Add(_xatanQ[4])
z = b1.Div(b2)
z = d.Mul(z).Add(d)
return z
@@ -1250,13 +1259,13 @@ func (d Decimal) satan() Decimal {
Tan3pio8 := NewFromFloat(2.41421356237309504880) // tan(3*pi/8)
pi := NewFromFloat(3.14159265358979323846264338327950288419716939937510582097494459)
- if d.LessThanOrEqual(NewFromFloat(0.66)) {
+ if d.LessThanOrEqual(New(66, -2)) {
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(Two).Sub(One.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))
+ return pi.Div(New(4, 0)).Add((d.Sub(One).Div(d.Add(One))).xatan()).Add(New(5, -1).Mul(Morebits))
}
// sin coefficients
@@ -1276,12 +1285,12 @@ func (d Decimal) Sin() Decimal {
PI4C := NewFromFloat(2.69515142907905952645e-15) // 0x3ce8469898cc5170,
M4PI := NewFromFloat(1.273239544735162542821171882678754627704620361328125) // 4/pi
- if d.Equal(NewFromFloat(0.0)) {
+ if d.IsZero() {
return d
}
// make argument positive but save the sign
sign := false
- if d.LessThan(NewFromFloat(0.0)) {
+ if d.IsNegative() {
d = d.Neg()
sign = true
}
@@ -1292,7 +1301,7 @@ func (d Decimal) Sin() Decimal {
// map zeros to origin
if j&1 == 1 {
j++
- y = y.Add(NewFromFloat(1.0))
+ y = y.Add(One)
}
j &= 7 // octant modulo 2Pi radians (360 degrees)
// reflect in x axis
@@ -1305,7 +1314,7 @@ func (d Decimal) Sin() Decimal {
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)
+ y = One.Sub(New(5, -1).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])))
}
@@ -1335,7 +1344,7 @@ func (d Decimal) Cos() Decimal {
// make argument positive
sign := false
- if d.LessThan(NewFromFloat(0.0)) {
+ if d.IsNegative() {
d = d.Neg()
}
@@ -1345,7 +1354,7 @@ func (d Decimal) Cos() Decimal {
// map zeros to origin
if j&1 == 1 {
j++
- y = y.Add(NewFromFloat(1.0))
+ y = y.Add(One)
}
j &= 7 // octant modulo 2Pi radians (360 degrees)
// reflect in x axis
@@ -1364,7 +1373,7 @@ func (d Decimal) Cos() Decimal {
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)
+ y = One.Sub(New(5, -1).Mul(zz)).Add(w)
}
if sign {
y = y.Neg()
@@ -1393,13 +1402,13 @@ func (d Decimal) Tan() Decimal {
PI4C := NewFromFloat(2.69515142907905952645e-15) // 0x3ce8469898cc5170,
M4PI := NewFromFloat(1.273239544735162542821171882678754627704620361328125) // 4/pi
- if d.Equal(NewFromFloat(0.0)) {
+ if d.IsZero() {
return d
}
// make argument positive but save the sign
sign := false
- if d.LessThan(NewFromFloat(0.0)) {
+ if d.IsNegative() {
d = d.Neg()
sign = true
}
@@ -1410,13 +1419,13 @@ func (d Decimal) Tan() Decimal {
// map zeros to origin
if j&1 == 1 {
j++
- y = y.Add(NewFromFloat(1.0))
+ y = y.Add(One)
}
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)) {
+ if zz.GreaterThan(New(1, -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)))
@@ -1424,7 +1433,7 @@ func (d Decimal) Tan() Decimal {
y = z
}
if j&2 == 2 {
- y = NewFromFloat(-1.0).Div(y)
+ y = New(-1, 0).Div(y)
}
if sign {
y = y.Neg()