Skip to content

Commit 719ede1

Browse files
committed
*: minor fixes to Log, Sqrt, Int64, ...
Fixes: #76 Updates: #46
1 parent c5c9073 commit 719ede1

File tree

10 files changed

+55
-71
lines changed

10 files changed

+55
-71
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,3 +41,4 @@ benchmarks/decbench
4141
*.class
4242
*.gz
4343
internal/nat/
44+
x.bash

big.go

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -759,7 +759,7 @@ func (x *Big) Int64() (int64, bool) {
759759
return 0, false
760760
}
761761

762-
// x might be too large to fit into an uint64 *now*, but rescaling x might
762+
// x might be too large to fit into an int64 *now*, but rescaling x might
763763
// shrink it enough. See issue #20.
764764
if !x.isCompact() {
765765
xb := x.Int(nil)
@@ -773,14 +773,11 @@ func (x *Big) Int64() (int64, bool) {
773773
return 0, false
774774
}
775775
}
776-
if u > math.MaxInt64 {
777-
return 0, false
778-
}
779-
b := int64(u)
780-
if x.form&signbit != 0 {
781-
b = -b
776+
su := int64(u)
777+
if su >= 0 || x.Signbit() && su == -su {
778+
return su, true
782779
}
783-
return b, true
780+
return 0, false
784781
}
785782

786783
// Uint64 returns x as an int64, truncating towards zero. The returned boolean

big_ctx.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -909,11 +909,12 @@ func (c Context) Round(z *Big) *Big {
909909
return z
910910
}
911911

912-
if z.Precision() <= n {
912+
zp := z.Precision()
913+
if zp <= n {
913914
return c.fix(z)
914915
}
915916

916-
shift := z.Precision() - n
917+
shift := zp - n
917918
if shift > c.maxScale() {
918919
return z.xflow(c.minScale(), false, true)
919920
}

internal/c/const_386.go renamed to internal/c/const32.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// +build 386,!amd64
1+
// +build 386 mips mipsle arm
22

33
package c
44

internal/c/const_amd64.go renamed to internal/c/const64.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// +build amd64,!386
1+
// +build ppc64le ppc64 amd64p32 s390x arm64 mips64 mips64le amd64
22

33
package c
44

math/const.go

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -103,26 +103,30 @@ func pi(z *decimal.Big, ctx decimal.Context) *decimal.Big {
103103
}
104104

105105
// ln10 sets z to log(10) and returns z.
106-
func ln10(z *decimal.Big, prec int) *decimal.Big {
106+
func ln10(z *decimal.Big, prec int, t *Term) *decimal.Big {
107107
ctx := decimal.Context{Precision: prec}
108108
if ctx.Precision <= constPrec {
109109
return ctx.Set(z, _Ln10)
110110
}
111111

112-
// TODO(eric): we can (possibly?) speed this up by selecting a log10 constant
113-
// that's some truncation of our continued fraction and setting the starting
114-
// term to that position in our continued fraction.
112+
// TODO(eric): we can speed this up by selecting a log10 constant that's
113+
// some truncation of our continued fraction and setting the starting term
114+
// to that position in our continued fraction.
115115

116116
ctx.Precision += 3
117117
g := lgen{
118118
ctx: ctx,
119119
pow: eightyOne, // 9 * 9
120120
z2: eleven, // 9 + 2
121121
k: -1,
122-
t: Term{A: new(decimal.Big), B: new(decimal.Big)},
123122
}
124-
ctx.Quo(z, eighteen /* 9 * 2 */, Lentz(z, &g))
125-
ctx.Precision -= 3
123+
if t != nil {
124+
g.t = *t
125+
} else {
126+
g.t = makeTerm()
127+
}
128+
ctx.Quo(z, eighteen /* 9 * 2 */, Wallis(z, &g))
129+
ctx.Precision = prec
126130
return ctx.Round(z)
127131
}
128132

math/continued_frac.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@ func (t Term) String() string {
1818
return fmt.Sprintf("[%s / %s]", t.A, t.B)
1919
}
2020

21+
func makeTerm() Term {
22+
return Term{A: new(decimal.Big), B: new(decimal.Big)}
23+
}
24+
2125
// Generator represents a continued fraction.
2226
type Generator interface {
2327
// Next returns true if there are future terms. Every call to Term—even the

math/log.go

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
package math
22

33
import (
4-
stdMath "math"
5-
64
"github.com/ericlagergren/decimal"
75
"github.com/ericlagergren/decimal/internal/arith"
86
"github.com/ericlagergren/decimal/internal/c"
@@ -41,7 +39,7 @@ func Log(z, x *decimal.Big) *decimal.Big {
4139
return z.SetMantScale(0, 0)
4240
case 10:
4341
// Specialized function.
44-
return ln10(z, precision(z))
42+
return ln10(z, precision(z), nil)
4543
}
4644
}
4745
}
@@ -77,8 +75,6 @@ func logSpecials(z, x *decimal.Big) bool {
7775
// log set z to log(x), or log10(x) if ten. It does not check for special values,
7876
// nor implement any special casing.
7977
func log(z, x *decimal.Big, ten bool) *decimal.Big {
80-
prec := precision(z)
81-
8278
t := int64(adjusted(x))
8379
if t < 0 {
8480
t = -t - 1
@@ -102,13 +98,15 @@ func log(z, x *decimal.Big, ten bool) *decimal.Big {
10298
// Compute
10399
// log(y) + p*log(10)
104100

105-
// TODO(eric): adj should be large enough. It's passed multiple iterations
106-
// of with a precision in [1, 5000) and a 128-bit decimal.
107-
adj := 7 + int(4*stdMath.Log(float64(x.Precision())))
101+
// TODO(eric): the precision adjustment should be large enough. It's passed
102+
// multiple iterations of with a precision in [1, 5000) and a 128-bit decimal.
103+
prec := precision(z)
104+
ctx := decimal.Context{
105+
Precision: prec + arith.Length(uint64(prec+x.Precision())) + 5,
106+
}
108107
if ten {
109-
adj += 3
108+
ctx.Precision += 3
110109
}
111-
ctx := decimal.Context{Precision: prec + adj}
112110

113111
var p int64
114112
switch {
@@ -118,15 +116,16 @@ func log(z, x *decimal.Big, ten bool) *decimal.Big {
118116
// 0.0001
119117
case x.Scale() >= x.Precision():
120118
p = -int64(x.Scale() - x.Precision() + 1)
119+
ctx.Precision = int(float64(ctx.Precision) * 1.5)
121120
// 12.345
122121
default:
123122
p = int64(-x.Scale() + x.Precision() - 1)
124123
}
125124

126-
// Rescale to 1 <= x <= 10
127-
y := decimal.WithContext(ctx).Copy(x).SetScale(x.Precision() - 1)
125+
// Rescale to 1 <= x < 10
126+
y := new(decimal.Big).Copy(x).SetScale(x.Precision() - 1)
128127
// Continued fraction algorithm is for log(1+x)
129-
y.Sub(y, one)
128+
ctx.Sub(y, y, one)
130129

131130
g := lgen{
132131
ctx: ctx,
@@ -140,10 +139,10 @@ func log(z, x *decimal.Big, ten bool) *decimal.Big {
140139
// better performance at ~750 digits of precision. Consider using Newton's
141140
// method or another algorithm for lower precision ranges.
142141

143-
ctx.Quo(z, y.Mul(y, two), Lentz(z, &g))
142+
ctx.Quo(z, ctx.Mul(y, y, two), Wallis(z, &g))
144143

145144
if p != 0 || ten {
146-
t := ln10(y, ctx.Precision) // recycle y
145+
t := ln10(y, ctx.Precision, &g.t) // recycle y, g.t
147146

148147
// Avoid doing unnecessary work.
149148
switch p {
@@ -164,7 +163,7 @@ func log(z, x *decimal.Big, ten bool) *decimal.Big {
164163
ctx.Quo(z, z, t)
165164
}
166165
}
167-
ctx.Precision -= adj
166+
ctx.Precision = prec
168167
return ctx.Round(z)
169168
}
170169

@@ -178,13 +177,14 @@ type lgen struct {
178177

179178
func (l *lgen) Context() decimal.Context { return l.ctx }
180179

181-
func (l *lgen) Lentz() (f, Δ, C, D, eps *decimal.Big) {
182-
f = decimal.WithContext(l.ctx)
183-
Δ = decimal.WithContext(l.ctx)
184-
C = decimal.WithContext(l.ctx)
185-
D = decimal.WithContext(l.ctx)
180+
func (l *lgen) Wallis() (a, a1, b, b1, p, eps *decimal.Big) {
181+
a = decimal.WithContext(l.ctx)
182+
a1 = decimal.WithContext(l.ctx)
183+
b = decimal.WithContext(l.ctx)
184+
b1 = decimal.WithContext(l.ctx)
185+
p = decimal.WithContext(l.ctx)
186186
eps = decimal.New(1, l.ctx.Precision)
187-
return
187+
return a, a1, b, b1, p, eps
188188
}
189189

190190
func (a *lgen) Next() bool { return true }

math/sqrt.go

Lines changed: 5 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,6 @@ func Sqrt(z, x *decimal.Big) *decimal.Big {
4747
if xs == 0 {
4848
return z.SetMantScale(0, ideal).CopySign(z, x)
4949
}
50-
// errors.New("math.Sqrt: cannot take square root of negative number"),
5150
z.Context.Conditions |= decimal.InvalidOperation
5251
return z.SetNaN(false)
5352
}
@@ -66,20 +65,7 @@ func Sqrt(z, x *decimal.Big) *decimal.Big {
6665

6766
// Fast path #1: use math.Sqrt if our decimal is small enough.
6867
if f, exact := x.Float64(); exact && prec <= 15 {
69-
ctx.Round(z.SetFloat64(math.Sqrt(f)))
70-
ctx.Precision = x.Precision()
71-
72-
var tmp decimal.Big
73-
if ctx.Mul(&tmp, z, z).Cmp(x) == 0 {
74-
ctx.Reduce(z)
75-
if !rnd {
76-
z.Context.Conditions &= ^decimal.Rounded
77-
}
78-
if !ixt {
79-
z.Context.Conditions &= ^decimal.Inexact
80-
}
81-
}
82-
return z
68+
return ctx.Reduce(z.SetFloat64(math.Sqrt(f)))
8369
}
8470

8571
// Source for the following algorithm:
@@ -128,26 +114,15 @@ func Sqrt(z, x *decimal.Big) *decimal.Big {
128114
// rounding mode half even (speleotrove.com/decimal/daops.html#refsqrt)
129115
// anyway.
130116

131-
z.SetScale(z.Scale() - e/2)
132-
if z.Precision() > prec {
133-
if !rnd {
134-
z.Context.Conditions &= ^decimal.Rounded
135-
}
136-
if !ixt {
137-
z.Context.Conditions &= ^decimal.Inexact
138-
}
139-
ctx.Precision = prec
140-
return ctx.Round(z)
141-
}
142-
// Perfect square.
143-
if ctx.Mul(&tmp, z, z).Cmp(x) == 0 {
144-
ctx.Reduce(z)
117+
ctx.Reduce(z.SetScale(z.Scale() - e/2))
118+
if z.Precision() <= prec {
145119
if !rnd {
146120
z.Context.Conditions &= ^decimal.Rounded
147121
}
148122
if !ixt {
149123
z.Context.Conditions &= ^decimal.Inexact
150124
}
151125
}
152-
return z
126+
ctx.Precision = prec
127+
return ctx.Round(z)
153128
}

scan.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -354,6 +354,8 @@ Loop:
354354
if err == nil {
355355
if scale != noScale {
356356
z.exp = -int(length - scale)
357+
} else {
358+
z.exp = 0
357359
}
358360
z.precision = arith.Length(z.compact)
359361
return nil

0 commit comments

Comments
 (0)