summaryrefslogtreecommitdiff
path: root/src/lib/math/log.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/math/log.go')
-rw-r--r--src/lib/math/log.go146
1 files changed, 109 insertions, 37 deletions
diff --git a/src/lib/math/log.go b/src/lib/math/log.go
index e51c72980..c0cfebf8b 100644
--- a/src/lib/math/log.go
+++ b/src/lib/math/log.go
@@ -4,56 +4,128 @@
package math
-/*
- * Log returns the natural logarithm of its floating
- * point argument.
- *
- * The coefficients are #2705 from Hart & Cheney. (19.38D)
- *
- * It calls frexp.
- */
+// The original C code, the long comment, and the constants
+// below are from FreeBSD's /usr/src/lib/msun/src/e_log.c
+// and came with this notice. The go code is a simpler
+// version of the original C.
+//
+// ====================================================
+// Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
+//
+// Developed at SunPro, a Sun Microsystems, Inc. business.
+// Permission to use, copy, modify, and distribute this
+// software is freely granted, provided that this notice
+// is preserved.
+// ====================================================
+//
+// __ieee754_log(x)
+// Return the logrithm of x
+//
+// Method :
+// 1. Argument Reduction: find k and f such that
+// x = 2^k * (1+f),
+// where sqrt(2)/2 < 1+f < sqrt(2) .
+//
+// 2. Approximation of log(1+f).
+// Let s = f/(2+f) ; based on log(1+f) = log(1+s) - log(1-s)
+// = 2s + 2/3 s**3 + 2/5 s**5 + .....,
+// = 2s + s*R
+// We use a special Reme algorithm on [0,0.1716] to generate
+// a polynomial of degree 14 to approximate R The maximum error
+// of this polynomial approximation is bounded by 2**-58.45. In
+// other words,
+// 2 4 6 8 10 12 14
+// R(z) ~ Lg1*s +Lg2*s +Lg3*s +Lg4*s +Lg5*s +Lg6*s +Lg7*s
+// (the values of Lg1 to Lg7 are listed in the program)
+// and
+// | 2 14 | -58.45
+// | Lg1*s +...+Lg7*s - R(z) | <= 2
+// | |
+// Note that 2s = f - s*f = f - hfsq + s*hfsq, where hfsq = f*f/2.
+// In order to guarantee error in log below 1ulp, we compute log
+// by
+// log(1+f) = f - s*(f - R) (if f is not too large)
+// log(1+f) = f - (hfsq - s*(hfsq+R)). (better accuracy)
+//
+// 3. Finally, log(x) = k*ln2 + log(1+f).
+// = k*ln2_hi+(f-(hfsq-(s*(hfsq+R)+k*ln2_lo)))
+// Here ln2 is split into two floating point number:
+// ln2_hi + ln2_lo,
+// where n*ln2_hi is always exact for |n| < 2000.
+//
+// Special cases:
+// log(x) is NaN with signal if x < 0 (including -INF) ;
+// log(+INF) is +INF; log(0) is -INF with signal;
+// log(NaN) is that NaN with no signal.
+//
+// Accuracy:
+// according to an error analysis, the error is always less than
+// 1 ulp (unit in the last place).
+//
+// Constants:
+// The hexadecimal values are the intended ones for the following
+// constants. The decimal values may be used, provided that the
+// compiler will convert from decimal to binary accurately enough
+// to produce the hexadecimal values shown.
-const
-(
- log2 = .693147180559945309e0;
- ln10u1 = .4342944819032518276511;
- sqrto2 = .707106781186547524e0;
- p0 = -.240139179559210510e2;
- p1 = .309572928215376501e2;
- p2 = -.963769093377840513e1;
- p3 = .421087371217979714e0;
- q0 = -.120069589779605255e2;
- q1 = .194809660700889731e2;
- q2 = -.891110902798312337e1;
+const (
+ Ln2Hi = 6.93147180369123816490e-01; /* 3fe62e42 fee00000 */
+ Ln2Lo = 1.90821492927058770002e-10; /* 3dea39ef 35793c76 */
+ Lg1 = 6.666666666666735130e-01; /* 3FE55555 55555593 */
+ Lg2 = 3.999999999940941908e-01; /* 3FD99999 9997FA04 */
+ Lg3 = 2.857142874366239149e-01; /* 3FD24924 94229359 */
+ Lg4 = 2.222219843214978396e-01; /* 3FCC71C5 1D8E78AF */
+ Lg5 = 1.818357216161805012e-01; /* 3FC74664 96CB03DE */
+ Lg6 = 1.531383769920937332e-01; /* 3FC39A09 D078C69F */
+ Lg7 = 1.479819860511658591e-01; /* 3FC2F112 DF3E5244 */
+
+ Two54 = 1<<54; // 2^54
+ TwoM20 = 1.0/(1<<20); // 2^-20
+ TwoM1022 = 2.2250738585072014e-308; // 2^-1022
+ Sqrt2 = 1.41421356237309504880168872420969808;
)
-export func Log(arg float64) float64 {
- if arg <= 0 {
+export func Log(x float64) float64 {
+ // special cases
+ switch {
+ case sys.isNaN(x) || sys.isInf(x, 1):
+ return x;
+ case x < 0:
return sys.NaN();
+ case x == 0:
+ return sys.Inf(-1);
}
- x, exp := sys.frexp(arg);
- for x < 0.5 {
- x = x*2;
- exp = exp-1;
- }
- if x < sqrto2 {
- x = x*2;
- exp = exp-1;
+ // reduce
+ f1, ki := sys.frexp(x);
+ if f1 < Sqrt2/2 {
+ f1 *= 2;
+ ki--;
}
+ f := f1 - 1;
+ k := float64(ki);
- z := (x-1) / (x+1);
- zsq := z*z;
-
- temp := ((p3*zsq + p2)*zsq + p1)*zsq + p0;
- temp = temp/(((zsq + q2)*zsq + q1)*zsq + q0);
- temp = temp*z + float64(exp)*log2;
- return temp;
+ // compute
+ s := f/(2+f);
+ s2 := s*s;
+ s4 := s2*s2;
+ t1 := s2*(Lg1 + s4*(Lg3 + s4*(Lg5 + s4*Lg7)));
+ t2 := s4*(Lg2 + s4*(Lg4 + s4*Lg6));
+ R := t1 + t2;
+ hfsq := 0.5*f*f;
+ return k*Ln2Hi - ((hfsq-(s*(hfsq+R)+k*Ln2Lo)) - f);
}
+const
+(
+ ln10u1 = .4342944819032518276511;
+)
+
export func Log10(arg float64) float64 {
if arg <= 0 {
return sys.NaN();
}
return Log(arg) * ln10u1;
}
+
+