Commit 4f3a641e authored by Russ Cox's avatar Russ Cox

math: fix Gamma(-171.5) on all platforms

Using 387 mode was computing it without underflow to zero,
apparently due to an 80-bit intermediate. Avoid underflow even
with 64-bit floats.

This eliminates the TODOs in the test suite.

Fixes linux-386-387 build and fixes #11441.

Change-Id: I8abaa63bfdf040438a95625d1cb61042f0302473
Reviewed-on: https://go-review.googlesource.com/30540
Run-TryBot: Russ Cox <rsc@golang.org>
Reviewed-by: 's avatarBrad Fitzpatrick <bradfitz@golang.org>
parent 20c48c95
...@@ -1235,9 +1235,9 @@ var vfgamma = [][2]float64{ ...@@ -1235,9 +1235,9 @@ var vfgamma = [][2]float64{
{-100.5, -3.3536908198076787e-159}, {-100.5, -3.3536908198076787e-159},
{-160.5, -5.255546447007829e-286}, {-160.5, -5.255546447007829e-286},
{-170.5, -3.3127395215386074e-308}, {-170.5, -3.3127395215386074e-308},
{-171.5, 0}, // TODO: 1.9316265431712e-310 {-171.5, 1.9316265431712e-310},
{-176.5, Copysign(0, -1)}, // TODO: -1.196e-321 {-176.5, -1.196e-321},
{-177.5, 0}, // TODO: 5e-324 {-177.5, 5e-324},
{-178.5, Copysign(0, -1)}, {-178.5, Copysign(0, -1)},
{-179.5, 0}, {-179.5, 0},
{-201.0001, 0}, {-201.0001, 0},
...@@ -1802,6 +1802,12 @@ var logbBC = []float64{ ...@@ -1802,6 +1802,12 @@ var logbBC = []float64{
} }
func tolerance(a, b, e float64) bool { func tolerance(a, b, e float64) bool {
// Multiplying by e here can underflow denormal values to zero.
// Check a==b so that at least if a and b are small and identical
// we say they match.
if a == b {
return true
}
d := a - b d := a - b
if d < 0 { if d < 0 {
d = -d d = -d
......
...@@ -91,10 +91,15 @@ var _gamS = [...]float64{ ...@@ -91,10 +91,15 @@ var _gamS = [...]float64{
} }
// Gamma function computed by Stirling's formula. // Gamma function computed by Stirling's formula.
// The polynomial is valid for 33 <= x <= 172. // The pair of results must be multiplied together to get the actual answer.
func stirling(x float64) float64 { // The multiplication is left to the caller so that, if careful, the caller can avoid
if x > 171.625 { // infinity for 172 <= x <= 180.
return Inf(1) // The polynomial is valid for 33 <= x <= 172; larger values are only used
// in reciprocal and produce denormalized floats. The lower precision there
// masks any imprecision in the polynomial.
func stirling(x float64) (float64, float64) {
if x > 200 {
return Inf(1), 1
} }
const ( const (
SqrtTwoPi = 2.506628274631000502417 SqrtTwoPi = 2.506628274631000502417
...@@ -102,15 +107,15 @@ func stirling(x float64) float64 { ...@@ -102,15 +107,15 @@ func stirling(x float64) float64 {
) )
w := 1 / x w := 1 / x
w = 1 + w*((((_gamS[0]*w+_gamS[1])*w+_gamS[2])*w+_gamS[3])*w+_gamS[4]) w = 1 + w*((((_gamS[0]*w+_gamS[1])*w+_gamS[2])*w+_gamS[3])*w+_gamS[4])
y := Exp(x) y1 := Exp(x)
y2 := 1.0
if x > MaxStirling { // avoid Pow() overflow if x > MaxStirling { // avoid Pow() overflow
v := Pow(x, 0.5*x-0.25) v := Pow(x, 0.5*x-0.25)
y = v * (v / y) y1, y2 = v, v/y1
} else { } else {
y = Pow(x, x-0.5) / y y1 = Pow(x, x-0.5) / y1
} }
y = SqrtTwoPi * y * w return y1, SqrtTwoPi * w * y2
return y
} }
// Gamma returns the Gamma function of x. // Gamma returns the Gamma function of x.
...@@ -138,7 +143,8 @@ func Gamma(x float64) float64 { ...@@ -138,7 +143,8 @@ func Gamma(x float64) float64 {
p := Floor(q) p := Floor(q)
if q > 33 { if q > 33 {
if x >= 0 { if x >= 0 {
return stirling(x) y1, y2 := stirling(x)
return y1 * y2
} }
// Note: x is negative but (checked above) not a negative integer, // Note: x is negative but (checked above) not a negative integer,
// so x must be small enough to be in range for conversion to int64. // so x must be small enough to be in range for conversion to int64.
...@@ -156,7 +162,14 @@ func Gamma(x float64) float64 { ...@@ -156,7 +162,14 @@ func Gamma(x float64) float64 {
if z == 0 { if z == 0 {
return Inf(signgam) return Inf(signgam)
} }
z = Pi / (Abs(z) * stirling(q)) sq1, sq2 := stirling(q)
absz := Abs(z)
d := absz * sq1 * sq2
if IsInf(d, 0) {
z = Pi / absz / sq1 / sq2
} else {
z = Pi / d
}
return float64(signgam) * z return float64(signgam) * z
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment