aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEoghan Sherry <ejsherry@gmail.com>2011-01-19 14:23:59 -0500
committerRuss Cox <rsc@golang.org>2011-01-19 14:23:59 -0500
commit13c2e629669c03b440fc1406aff14f72b9450a48 (patch)
tree96df4d02ac8a69d77c92299ce561d33ffc7980dc
parent01fad6a6b052292e598e78b30efb89a12ba1ea0e (diff)
downloadgo-13c2e629669c03b440fc1406aff14f72b9450a48.tar.gz
go-13c2e629669c03b440fc1406aff14f72b9450a48.zip
math: handle denormals in Frexp, Ilogb, Ldexp, and Logb
Also: * document special cases for Frexp and Ldexp * handle ±Inf in Ldexp * correctly return -0 on underflow in Ldexp * test special cases for Ldexp * test boundary cases for Frexp, Ilogb, Ldexp, and Logb R=rsc CC=golang-dev https://golang.org/cl/3676041
-rw-r--r--src/pkg/math/all_test.go116
-rw-r--r--src/pkg/math/bits.go10
-rw-r--r--src/pkg/math/frexp.go8
-rw-r--r--src/pkg/math/ldexp.go28
-rw-r--r--src/pkg/math/logb.go15
5 files changed, 164 insertions, 13 deletions
diff --git a/src/pkg/math/all_test.go b/src/pkg/math/all_test.go
index 6033d37e32..0efef2bbf0 100644
--- a/src/pkg/math/all_test.go
+++ b/src/pkg/math/all_test.go
@@ -1112,6 +1112,33 @@ var jM3SC = []float64{
NaN(),
}
+var vfldexpSC = []fi{
+ {0, 0},
+ {0, -1075},
+ {0, 1024},
+ {Copysign(0, -1), 0},
+ {Copysign(0, -1), -1075},
+ {Copysign(0, -1), 1024},
+ {Inf(1), 0},
+ {Inf(1), -1024},
+ {Inf(-1), 0},
+ {Inf(-1), -1024},
+ {NaN(), -1024},
+}
+var ldexpSC = []float64{
+ 0,
+ 0,
+ 0,
+ Copysign(0, -1),
+ Copysign(0, -1),
+ Copysign(0, -1),
+ Inf(1),
+ Inf(1),
+ Inf(-1),
+ Inf(-1),
+ NaN(),
+}
+
var vflgammaSC = []float64{
Inf(-1),
-3,
@@ -1440,6 +1467,65 @@ var yM3SC = []float64{
NaN(),
}
+// arguments and expected results for boundary cases
+const (
+ SmallestNormalFloat64 = 2.2250738585072014e-308 // 2**-1022
+ LargestSubnormalFloat64 = SmallestNormalFloat64 - SmallestNonzeroFloat64
+)
+
+var vffrexpBC = []float64{
+ SmallestNormalFloat64,
+ LargestSubnormalFloat64,
+ SmallestNonzeroFloat64,
+ MaxFloat64,
+ -SmallestNormalFloat64,
+ -LargestSubnormalFloat64,
+ -SmallestNonzeroFloat64,
+ -MaxFloat64,
+}
+var frexpBC = []fi{
+ {0.5, -1021},
+ {0.99999999999999978, -1022},
+ {0.5, -1073},
+ {0.99999999999999989, 1024},
+ {-0.5, -1021},
+ {-0.99999999999999978, -1022},
+ {-0.5, -1073},
+ {-0.99999999999999989, 1024},
+}
+
+var vfldexpBC = []fi{
+ {SmallestNormalFloat64, -52},
+ {LargestSubnormalFloat64, -51},
+ {SmallestNonzeroFloat64, 1074},
+ {MaxFloat64, -(1023 + 1074)},
+ {1, -1075},
+ {-1, -1075},
+ {1, 1024},
+ {-1, 1024},
+}
+var ldexpBC = []float64{
+ SmallestNonzeroFloat64,
+ 1e-323, // 2**-1073
+ 1,
+ 1e-323, // 2**-1073
+ 0,
+ Copysign(0, -1),
+ Inf(1),
+ Inf(-1),
+}
+
+var logbBC = []float64{
+ -1022,
+ -1023,
+ -1074,
+ 1023,
+ -1022,
+ -1023,
+ -1074,
+ 1023,
+}
+
func tolerance(a, b, e float64) bool {
d := a - b
if d < 0 {
@@ -1792,6 +1878,11 @@ func TestFrexp(t *testing.T) {
t.Errorf("Frexp(%g) = %g, %d, want %g, %d", vffrexpSC[i], f, j, frexpSC[i].f, frexpSC[i].i)
}
}
+ for i := 0; i < len(vffrexpBC); i++ {
+ if f, j := Frexp(vffrexpBC[i]); !alike(frexpBC[i].f, f) || frexpBC[i].i != j {
+ t.Errorf("Frexp(%g) = %g, %d, want %g, %d", vffrexpBC[i], f, j, frexpBC[i].f, frexpBC[i].i)
+ }
+ }
}
func TestGamma(t *testing.T) {
@@ -1833,6 +1924,11 @@ func TestIlogb(t *testing.T) {
t.Errorf("Ilogb(%g) = %d, want %d", vflogbSC[i], e, ilogbSC[i])
}
}
+ for i := 0; i < len(vffrexpBC); i++ {
+ if e := Ilogb(vffrexpBC[i]); int(logbBC[i]) != e {
+ t.Errorf("Ilogb(%g) = %d, want %d", vffrexpBC[i], e, int(logbBC[i]))
+ }
+ }
}
func TestJ0(t *testing.T) {
@@ -1891,6 +1987,21 @@ func TestLdexp(t *testing.T) {
t.Errorf("Ldexp(%g, %d) = %g, want %g", frexpSC[i].f, frexpSC[i].i, f, vffrexpSC[i])
}
}
+ for i := 0; i < len(vfldexpSC); i++ {
+ if f := Ldexp(vfldexpSC[i].f, vfldexpSC[i].i); !alike(ldexpSC[i], f) {
+ t.Errorf("Ldexp(%g, %d) = %g, want %g", vfldexpSC[i].f, vfldexpSC[i].i, f, ldexpSC[i])
+ }
+ }
+ for i := 0; i < len(vffrexpBC); i++ {
+ if f := Ldexp(frexpBC[i].f, frexpBC[i].i); !alike(vffrexpBC[i], f) {
+ t.Errorf("Ldexp(%g, %d) = %g, want %g", frexpBC[i].f, frexpBC[i].i, f, vffrexpBC[i])
+ }
+ }
+ for i := 0; i < len(vfldexpBC); i++ {
+ if f := Ldexp(vfldexpBC[i].f, vfldexpBC[i].i); !alike(ldexpBC[i], f) {
+ t.Errorf("Ldexp(%g, %d) = %g, want %g", vfldexpBC[i].f, vfldexpBC[i].i, f, ldexpBC[i])
+ }
+ }
}
func TestLgamma(t *testing.T) {
@@ -1934,6 +2045,11 @@ func TestLogb(t *testing.T) {
t.Errorf("Logb(%g) = %g, want %g", vflogbSC[i], f, logbSC[i])
}
}
+ for i := 0; i < len(vffrexpBC); i++ {
+ if e := Logb(vffrexpBC[i]); !alike(logbBC[i], e) {
+ t.Errorf("Ilogb(%g) = %g, want %g", vffrexpBC[i], e, logbBC[i])
+ }
+ }
}
func TestLog10(t *testing.T) {
diff --git a/src/pkg/math/bits.go b/src/pkg/math/bits.go
index 1a97e76799..a1dca3ed69 100644
--- a/src/pkg/math/bits.go
+++ b/src/pkg/math/bits.go
@@ -47,3 +47,13 @@ func IsInf(f float64, sign int) bool {
// return sign >= 0 && x == uvinf || sign <= 0 && x == uvneginf;
return sign >= 0 && f > MaxFloat64 || sign <= 0 && f < -MaxFloat64
}
+
+// normalize returns a normal number y and exponent exp
+// satisfying x == y × 2**exp. It assumes x is finite and non-zero.
+func normalize(x float64) (y float64, exp int) {
+ const SmallestNormal = 2.2250738585072014e-308 // 2**-1022
+ if Fabs(x) < SmallestNormal {
+ return x * (1 << 52), -52
+ }
+ return x, 0
+}
diff --git a/src/pkg/math/frexp.go b/src/pkg/math/frexp.go
index 203219c0dc..867b78f364 100644
--- a/src/pkg/math/frexp.go
+++ b/src/pkg/math/frexp.go
@@ -8,6 +8,11 @@ package math
// and an integral power of two.
// It returns frac and exp satisfying f == frac × 2**exp,
// with the absolute value of frac in the interval [½, 1).
+//
+// Special cases are:
+// Frexp(±0) = ±0, 0
+// Frexp(±Inf) = ±Inf, 0
+// Frexp(NaN) = NaN, 0
func Frexp(f float64) (frac float64, exp int) {
// TODO(rsc): Remove manual inlining of IsNaN, IsInf
// when compiler does it for us
@@ -18,8 +23,9 @@ func Frexp(f float64) (frac float64, exp int) {
case f < -MaxFloat64 || f > MaxFloat64 || f != f: // IsInf(f, 0) || IsNaN(f):
return f, 0
}
+ f, exp = normalize(f)
x := Float64bits(f)
- exp = int((x>>shift)&mask) - bias + 1
+ exp += int((x>>shift)&mask) - bias + 1
x &^= mask << shift
x |= (-1 + bias) << shift
frac = Float64frombits(x)
diff --git a/src/pkg/math/ldexp.go b/src/pkg/math/ldexp.go
index d04bf1581a..96c95cad4a 100644
--- a/src/pkg/math/ldexp.go
+++ b/src/pkg/math/ldexp.go
@@ -6,6 +6,11 @@ package math
// Ldexp is the inverse of Frexp.
// It returns frac × 2**exp.
+//
+// Special cases are:
+// Ldexp(±0, exp) = ±0
+// Ldexp(±Inf, exp) = ±Inf
+// Ldexp(NaN, exp) = NaN
func Ldexp(frac float64, exp int) float64 {
// TODO(rsc): Remove manual inlining of IsNaN, IsInf
// when compiler does it for us
@@ -13,21 +18,28 @@ func Ldexp(frac float64, exp int) float64 {
switch {
case frac == 0:
return frac // correctly return -0
- case frac != frac: // IsNaN(frac):
- return NaN()
+ case frac < -MaxFloat64 || frac > MaxFloat64 || frac != frac: // IsInf(frac, 0) || IsNaN(frac):
+ return frac
}
+ frac, e := normalize(frac)
+ exp += e
x := Float64bits(frac)
- exp += int(x>>shift) & mask
- if exp <= 0 {
- return 0 // underflow
+ exp += int(x>>shift)&mask - bias
+ if exp < -1074 {
+ return Copysign(0, frac) // underflow
}
- if exp >= mask { // overflow
+ if exp > 1023 { // overflow
if frac < 0 {
return Inf(-1)
}
return Inf(1)
}
+ var m float64 = 1
+ if exp < -1022 { // denormal
+ exp += 52
+ m = 1.0 / (1 << 52) // 2**-52
+ }
x &^= mask << shift
- x |= uint64(exp) << shift
- return Float64frombits(x)
+ x |= uint64(exp+bias) << shift
+ return m * Float64frombits(x)
}
diff --git a/src/pkg/math/logb.go b/src/pkg/math/logb.go
index 9e46515171..072281ddf9 100644
--- a/src/pkg/math/logb.go
+++ b/src/pkg/math/logb.go
@@ -4,7 +4,7 @@
package math
-// Logb(x) returns the binary exponent of non-zero x.
+// Logb(x) returns the binary exponent of x.
//
// Special cases are:
// Logb(±Inf) = +Inf
@@ -22,10 +22,10 @@ func Logb(x float64) float64 {
case x != x: // IsNaN(x):
return x
}
- return float64(int((Float64bits(x)>>shift)&mask) - bias)
+ return float64(ilogb(x))
}
-// Ilogb(x) returns the binary exponent of non-zero x as an integer.
+// Ilogb(x) returns the binary exponent of x as an integer.
//
// Special cases are:
// Ilogb(±Inf) = MaxInt32
@@ -43,5 +43,12 @@ func Ilogb(x float64) int {
case x < -MaxFloat64 || x > MaxFloat64: // IsInf(x, 0):
return MaxInt32
}
- return int((Float64bits(x)>>shift)&mask) - bias
+ return ilogb(x)
+}
+
+// logb returns the binary exponent of x. It assumes x is finite and
+// non-zero.
+func ilogb(x float64) int {
+ x, exp := normalize(x)
+ return int((Float64bits(x)>>shift)&mask) - bias + exp
}