Skip to content

Commit ff4e08f

Browse files
ejsherryrsc
authored andcommitted
math: improve accuracy of Exp2
Note: * Exp2 doesn't have a special case for very small arguments * Exp2 hasn't been subject to a proper error analysis Also: * add tests for Exp2 with integer argument * always test Go versions of Exp and Exp2 R=rsc CC=Charlie Dorian, PeterGo, golang-dev https://golang.org/cl/3481041
1 parent 6eee9ed commit ff4e08f

File tree

6 files changed

+238
-133
lines changed

6 files changed

+238
-133
lines changed

src/pkg/math/Makefile

+1
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ ALLGOFILES=\
5454
copysign.go\
5555
erf.go\
5656
exp.go\
57+
exp_port.go\
5758
exp2.go\
5859
expm1.go\
5960
fabs.go\

src/pkg/math/all_test.go

+33-4
Original file line numberDiff line numberDiff line change
@@ -1662,14 +1662,19 @@ func TestErfc(t *testing.T) {
16621662
}
16631663

16641664
func TestExp(t *testing.T) {
1665+
testExp(t, Exp, "Exp")
1666+
testExp(t, ExpGo, "ExpGo")
1667+
}
1668+
1669+
func testExp(t *testing.T, Exp func(float64) float64, name string) {
16651670
for i := 0; i < len(vf); i++ {
16661671
if f := Exp(vf[i]); !close(exp[i], f) {
1667-
t.Errorf("Exp(%g) = %g, want %g", vf[i], f, exp[i])
1672+
t.Errorf("%s(%g) = %g, want %g", name, vf[i], f, exp[i])
16681673
}
16691674
}
16701675
for i := 0; i < len(vfexpSC); i++ {
16711676
if f := Exp(vfexpSC[i]); !alike(expSC[i], f) {
1672-
t.Errorf("Exp(%g) = %g, want %g", vfexpSC[i], f, expSC[i])
1677+
t.Errorf("%s(%g) = %g, want %g", name, vfexpSC[i], f, expSC[i])
16731678
}
16741679
}
16751680
}
@@ -1689,14 +1694,26 @@ func TestExpm1(t *testing.T) {
16891694
}
16901695

16911696
func TestExp2(t *testing.T) {
1697+
testExp2(t, Exp2, "Exp2")
1698+
testExp2(t, Exp2Go, "Exp2Go")
1699+
}
1700+
1701+
func testExp2(t *testing.T, Exp2 func(float64) float64, name string) {
16921702
for i := 0; i < len(vf); i++ {
16931703
if f := Exp2(vf[i]); !close(exp2[i], f) {
1694-
t.Errorf("Exp2(%g) = %g, want %g", vf[i], f, exp2[i])
1704+
t.Errorf("%s(%g) = %g, want %g", name, vf[i], f, exp2[i])
16951705
}
16961706
}
16971707
for i := 0; i < len(vfexpSC); i++ {
16981708
if f := Exp2(vfexpSC[i]); !alike(expSC[i], f) {
1699-
t.Errorf("Exp2(%g) = %g, want %g", vfexpSC[i], f, expSC[i])
1709+
t.Errorf("%s(%g) = %g, want %g", name, vfexpSC[i], f, expSC[i])
1710+
}
1711+
}
1712+
for n := -1074; n < 1024; n++ {
1713+
f := Exp2(float64(n))
1714+
vf := Ldexp(1, n)
1715+
if f != vf {
1716+
t.Errorf("%s(%d) = %g, want %g", name, n, f, vf)
17001717
}
17011718
}
17021719
}
@@ -2352,6 +2369,12 @@ func BenchmarkExp(b *testing.B) {
23522369
}
23532370
}
23542371

2372+
func BenchmarkExpGo(b *testing.B) {
2373+
for i := 0; i < b.N; i++ {
2374+
ExpGo(.5)
2375+
}
2376+
}
2377+
23552378
func BenchmarkExpm1(b *testing.B) {
23562379
for i := 0; i < b.N; i++ {
23572380
Expm1(.5)
@@ -2364,6 +2387,12 @@ func BenchmarkExp2(b *testing.B) {
23642387
}
23652388
}
23662389

2390+
func BenchmarkExp2Go(b *testing.B) {
2391+
for i := 0; i < b.N; i++ {
2392+
Exp2Go(.5)
2393+
}
2394+
}
2395+
23672396
func BenchmarkFabs(b *testing.B) {
23682397
for i := 0; i < b.N; i++ {
23692398
Fabs(.5)

src/pkg/math/exp.go

+1-128
Original file line numberDiff line numberDiff line change
@@ -4,138 +4,11 @@
44

55
package math
66

7-
8-
// The original C code, the long comment, and the constants
9-
// below are from FreeBSD's /usr/src/lib/msun/src/e_exp.c
10-
// and came with this notice. The go code is a simplified
11-
// version of the original C.
12-
//
13-
// ====================================================
14-
// Copyright (C) 2004 by Sun Microsystems, Inc. All rights reserved.
15-
//
16-
// Permission to use, copy, modify, and distribute this
17-
// software is freely granted, provided that this notice
18-
// is preserved.
19-
// ====================================================
20-
//
21-
//
22-
// exp(x)
23-
// Returns the exponential of x.
24-
//
25-
// Method
26-
// 1. Argument reduction:
27-
// Reduce x to an r so that |r| <= 0.5*ln2 ~ 0.34658.
28-
// Given x, find r and integer k such that
29-
//
30-
// x = k*ln2 + r, |r| <= 0.5*ln2.
31-
//
32-
// Here r will be represented as r = hi-lo for better
33-
// accuracy.
34-
//
35-
// 2. Approximation of exp(r) by a special rational function on
36-
// the interval [0,0.34658]:
37-
// Write
38-
// R(r**2) = r*(exp(r)+1)/(exp(r)-1) = 2 + r*r/6 - r**4/360 + ...
39-
// We use a special Remes algorithm on [0,0.34658] to generate
40-
// a polynomial of degree 5 to approximate R. The maximum error
41-
// of this polynomial approximation is bounded by 2**-59. In
42-
// other words,
43-
// R(z) ~ 2.0 + P1*z + P2*z**2 + P3*z**3 + P4*z**4 + P5*z**5
44-
// (where z=r*r, and the values of P1 to P5 are listed below)
45-
// and
46-
// | 5 | -59
47-
// | 2.0+P1*z+...+P5*z - R(z) | <= 2
48-
// | |
49-
// The computation of exp(r) thus becomes
50-
// 2*r
51-
// exp(r) = 1 + -------
52-
// R - r
53-
// r*R1(r)
54-
// = 1 + r + ----------- (for better accuracy)
55-
// 2 - R1(r)
56-
// where
57-
// 2 4 10
58-
// R1(r) = r - (P1*r + P2*r + ... + P5*r ).
59-
//
60-
// 3. Scale back to obtain exp(x):
61-
// From step 1, we have
62-
// exp(x) = 2**k * exp(r)
63-
//
64-
// Special cases:
65-
// exp(INF) is INF, exp(NaN) is NaN;
66-
// exp(-INF) is 0, and
67-
// for finite argument, only exp(0)=1 is exact.
68-
//
69-
// Accuracy:
70-
// according to an error analysis, the error is always less than
71-
// 1 ulp (unit in the last place).
72-
//
73-
// Misc. info.
74-
// For IEEE double
75-
// if x > 7.09782712893383973096e+02 then exp(x) overflow
76-
// if x < -7.45133219101941108420e+02 then exp(x) underflow
77-
//
78-
// Constants:
79-
// The hexadecimal values are the intended ones for the following
80-
// constants. The decimal values may be used, provided that the
81-
// compiler will convert from decimal to binary accurately enough
82-
// to produce the hexadecimal values shown.
83-
847
// Exp returns e**x, the base-e exponential of x.
858
//
869
// Special cases are:
8710
// Exp(+Inf) = +Inf
8811
// Exp(NaN) = NaN
8912
// Very large values overflow to 0 or +Inf.
9013
// Very small values underflow to 1.
91-
func Exp(x float64) float64 {
92-
const (
93-
Ln2Hi = 6.93147180369123816490e-01
94-
Ln2Lo = 1.90821492927058770002e-10
95-
Log2e = 1.44269504088896338700e+00
96-
P1 = 1.66666666666666019037e-01 /* 0x3FC55555; 0x5555553E */
97-
P2 = -2.77777777770155933842e-03 /* 0xBF66C16C; 0x16BEBD93 */
98-
P3 = 6.61375632143793436117e-05 /* 0x3F11566A; 0xAF25DE2C */
99-
P4 = -1.65339022054652515390e-06 /* 0xBEBBBD41; 0xC5D26BF1 */
100-
P5 = 4.13813679705723846039e-08 /* 0x3E663769; 0x72BEA4D0 */
101-
102-
Overflow = 7.09782712893383973096e+02
103-
Underflow = -7.45133219101941108420e+02
104-
NearZero = 1.0 / (1 << 28) // 2**-28
105-
)
106-
107-
// TODO(rsc): Remove manual inlining of IsNaN, IsInf
108-
// when compiler does it for us
109-
// special cases
110-
switch {
111-
case x != x || x > MaxFloat64: // IsNaN(x) || IsInf(x, 1):
112-
return x
113-
case x < -MaxFloat64: // IsInf(x, -1):
114-
return 0
115-
case x > Overflow:
116-
return Inf(1)
117-
case x < Underflow:
118-
return 0
119-
case -NearZero < x && x < NearZero:
120-
return 1
121-
}
122-
123-
// reduce; computed as r = hi - lo for extra precision.
124-
var k int
125-
switch {
126-
case x < 0:
127-
k = int(Log2e*x - 0.5)
128-
case x > 0:
129-
k = int(Log2e*x + 0.5)
130-
}
131-
hi := x - float64(k)*Ln2Hi
132-
lo := float64(k) * Ln2Lo
133-
r := hi - lo
134-
135-
// compute
136-
t := r * r
137-
c := r - t*(P1+t*(P2+t*(P3+t*(P4+t*P5))))
138-
y := 1 - ((lo - (r*c)/(2-c)) - hi)
139-
// TODO(rsc): make sure Ldexp can handle boundary k
140-
return Ldexp(y, k)
141-
}
14+
func Exp(x float64) float64 { return expGo(x) }

src/pkg/math/exp2.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,4 @@ package math
77
// Exp2 returns 2**x, the base-2 exponential of x.
88
//
99
// Special cases are the same as Exp.
10-
func Exp2(x float64) float64 { return Exp(x * Ln2) }
10+
func Exp2(x float64) float64 { return exp2Go(x) }

0 commit comments

Comments
 (0)