mirror of
https://github.com/golang/go
synced 2024-11-22 03:24:41 -07:00
math: special cases for Atan, Asin and Acos
Added tests for NaN and out-of-range values. Combined asin.go and atan.go into atan.go. R=rsc CC=golang-dev https://golang.org/cl/180065
This commit is contained in:
parent
7ec0856f01
commit
fd1db67e87
@ -22,6 +22,18 @@ var vf = []float64{
|
|||||||
1.8253080916808550e+00,
|
1.8253080916808550e+00,
|
||||||
-8.6859247685756013e+00,
|
-8.6859247685756013e+00,
|
||||||
}
|
}
|
||||||
|
var acos = []float64{
|
||||||
|
1.0496193546107222e+00,
|
||||||
|
6.858401281366443e-01,
|
||||||
|
1.598487871457716e+00,
|
||||||
|
2.095619936147586e+00,
|
||||||
|
2.7053008467824158e-01,
|
||||||
|
1.2738121680361776e+00,
|
||||||
|
1.0205369421140630e+00,
|
||||||
|
1.2945003481781246e+00,
|
||||||
|
1.3872364345374451e+00,
|
||||||
|
2.6231510803970464e+00,
|
||||||
|
}
|
||||||
var asin = []float64{
|
var asin = []float64{
|
||||||
5.2117697218417440e-01,
|
5.2117697218417440e-01,
|
||||||
8.8495619865825236e-01,
|
8.8495619865825236e-01,
|
||||||
@ -154,39 +166,26 @@ var tanh = []float64{
|
|||||||
9.4936501296239700e-01,
|
9.4936501296239700e-01,
|
||||||
-9.9999994291374019e-01,
|
-9.9999994291374019e-01,
|
||||||
}
|
}
|
||||||
var vfsin = []float64{
|
|
||||||
NaN(),
|
// arguments and expected results for special cases
|
||||||
Inf(-1),
|
var vfasinSC = []float64{
|
||||||
0,
|
|
||||||
Inf(1),
|
|
||||||
}
|
|
||||||
var vfasin = []float64{
|
|
||||||
NaN(),
|
NaN(),
|
||||||
-Pi,
|
-Pi,
|
||||||
0,
|
|
||||||
Pi,
|
Pi,
|
||||||
}
|
}
|
||||||
var vf1 = []float64{
|
var asinSC = []float64{
|
||||||
|
NaN(),
|
||||||
|
NaN(),
|
||||||
NaN(),
|
NaN(),
|
||||||
Inf(-1),
|
|
||||||
-Pi,
|
|
||||||
-1,
|
|
||||||
0,
|
|
||||||
1,
|
|
||||||
Pi,
|
|
||||||
Inf(1),
|
|
||||||
}
|
}
|
||||||
var vfhypot = [][2]float64{
|
|
||||||
[2]float64{Inf(-1), 1},
|
var vfatanSC = []float64{
|
||||||
[2]float64{Inf(1), 1},
|
NaN(),
|
||||||
[2]float64{1, Inf(-1)},
|
|
||||||
[2]float64{1, Inf(1)},
|
|
||||||
[2]float64{NaN(), Inf(-1)},
|
|
||||||
[2]float64{NaN(), Inf(1)},
|
|
||||||
[2]float64{1, NaN()},
|
|
||||||
[2]float64{NaN(), 1},
|
|
||||||
}
|
}
|
||||||
var vf2 = [][2]float64{
|
var atanSC = []float64{
|
||||||
|
NaN(),
|
||||||
|
}
|
||||||
|
var vfpowSC = [][2]float64{
|
||||||
[2]float64{-Pi, Pi},
|
[2]float64{-Pi, Pi},
|
||||||
[2]float64{-Pi, -Pi},
|
[2]float64{-Pi, -Pi},
|
||||||
[2]float64{Inf(-1), 3},
|
[2]float64{Inf(-1), 3},
|
||||||
@ -230,7 +229,7 @@ var vf2 = [][2]float64{
|
|||||||
[2]float64{Inf(1), 0},
|
[2]float64{Inf(1), 0},
|
||||||
[2]float64{NaN(), 0},
|
[2]float64{NaN(), 0},
|
||||||
}
|
}
|
||||||
var pow2 = []float64{
|
var powSC = []float64{
|
||||||
NaN(),
|
NaN(),
|
||||||
NaN(),
|
NaN(),
|
||||||
Inf(-1),
|
Inf(-1),
|
||||||
@ -306,12 +305,31 @@ func alike(a, b float64) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestAcos(t *testing.T) {
|
||||||
|
for i := 0; i < len(vf); i++ {
|
||||||
|
// if f := Acos(vf[i] / 10); !veryclose(acos[i], f) {
|
||||||
|
if f := Acos(vf[i] / 10); !close(acos[i], f) {
|
||||||
|
t.Errorf("Acos(%g) = %g, want %g\n", vf[i]/10, f, acos[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for i := 0; i < len(vfasinSC); i++ {
|
||||||
|
if f := Acos(vfasinSC[i]); !alike(asinSC[i], f) {
|
||||||
|
t.Errorf("Acos(%g) = %g, want %g\n", vfasinSC[i], f, asinSC[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestAsin(t *testing.T) {
|
func TestAsin(t *testing.T) {
|
||||||
for i := 0; i < len(vf); i++ {
|
for i := 0; i < len(vf); i++ {
|
||||||
if f := Asin(vf[i] / 10); !veryclose(asin[i], f) {
|
if f := Asin(vf[i] / 10); !veryclose(asin[i], f) {
|
||||||
t.Errorf("Asin(%g) = %g, want %g\n", vf[i]/10, f, asin[i])
|
t.Errorf("Asin(%g) = %g, want %g\n", vf[i]/10, f, asin[i])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for i := 0; i < len(vfasinSC); i++ {
|
||||||
|
if f := Asin(vfasinSC[i]); !alike(asinSC[i], f) {
|
||||||
|
t.Errorf("Asin(%g) = %g, want %g\n", vfasinSC[i], f, asinSC[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAtan(t *testing.T) {
|
func TestAtan(t *testing.T) {
|
||||||
@ -320,6 +338,11 @@ func TestAtan(t *testing.T) {
|
|||||||
t.Errorf("Atan(%g) = %g, want %g\n", vf[i], f, atan[i])
|
t.Errorf("Atan(%g) = %g, want %g\n", vf[i], f, atan[i])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for i := 0; i < len(vfatanSC); i++ {
|
||||||
|
if f := Atan(vfatanSC[i]); !alike(atanSC[i], f) {
|
||||||
|
t.Errorf("Atan(%g) = %g, want %g\n", vfatanSC[i], f, atanSC[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestExp(t *testing.T) {
|
func TestExp(t *testing.T) {
|
||||||
@ -356,9 +379,9 @@ func TestPow(t *testing.T) {
|
|||||||
t.Errorf("Pow(10, %.17g) = %.17g, want %.17g\n", vf[i], f, pow[i])
|
t.Errorf("Pow(10, %.17g) = %.17g, want %.17g\n", vf[i], f, pow[i])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for i := 0; i < len(vf2); i++ {
|
for i := 0; i < len(vfpowSC); i++ {
|
||||||
if f := Pow(vf2[i][0], vf2[i][1]); !alike(pow2[i], f) {
|
if f := Pow(vfpowSC[i][0], vfpowSC[i][1]); !alike(powSC[i], f) {
|
||||||
t.Errorf("Pow(%.17g, %.17g) = %.17g, want %.17g\n", vf2[i][0], vf2[i][1], f, pow2[i])
|
t.Errorf("Pow(%.17g, %.17g) = %.17g, want %.17g\n", vfpowSC[i][0], vfpowSC[i][1], f, powSC[i])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -421,7 +444,7 @@ func TestLargeSin(t *testing.T) {
|
|||||||
f1 := Sin(vf[i])
|
f1 := Sin(vf[i])
|
||||||
f2 := Sin(vf[i] + large)
|
f2 := Sin(vf[i] + large)
|
||||||
if !kindaclose(f1, f2) {
|
if !kindaclose(f1, f2) {
|
||||||
t.Errorf("Sin(%g) = %g, want %g\n", vf[i]+large, f1, f2)
|
t.Errorf("Sin(%g) = %g, want %g\n", vf[i]+large, f2, f1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -432,7 +455,7 @@ func TestLargeCos(t *testing.T) {
|
|||||||
f1 := Cos(vf[i])
|
f1 := Cos(vf[i])
|
||||||
f2 := Cos(vf[i] + large)
|
f2 := Cos(vf[i] + large)
|
||||||
if !kindaclose(f1, f2) {
|
if !kindaclose(f1, f2) {
|
||||||
t.Errorf("Cos(%g) = %g, want %g\n", vf[i]+large, f1, f2)
|
t.Errorf("Cos(%g) = %g, want %g\n", vf[i]+large, f2, f1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -444,7 +467,7 @@ func TestLargeTan(t *testing.T) {
|
|||||||
f1 := Tan(vf[i])
|
f1 := Tan(vf[i])
|
||||||
f2 := Tan(vf[i] + large)
|
f2 := Tan(vf[i] + large)
|
||||||
if !kindaclose(f1, f2) {
|
if !kindaclose(f1, f2) {
|
||||||
t.Errorf("Tan(%g) = %g, want %g\n", vf[i]+large, f1, f2)
|
t.Errorf("Tan(%g) = %g, want %g\n", vf[i]+large, f2, f1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -488,3 +511,21 @@ func BenchmarkPowFrac(b *testing.B) {
|
|||||||
Pow(2.5, 1.5)
|
Pow(2.5, 1.5)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func BenchmarkAtan(b *testing.B) {
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
Atan(.5)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkAsin(b *testing.B) {
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
Asin(.5)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkAcos(b *testing.B) {
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
Acos(.5)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -25,9 +25,9 @@ func Asin(x float64) float64 {
|
|||||||
|
|
||||||
temp := Sqrt(1 - x*x)
|
temp := Sqrt(1 - x*x)
|
||||||
if x > 0.7 {
|
if x > 0.7 {
|
||||||
temp = Pi/2 - Atan(temp/x)
|
temp = Pi/2 - satan(temp/x)
|
||||||
} else {
|
} else {
|
||||||
temp = Atan(x / temp)
|
temp = satan(x / temp)
|
||||||
}
|
}
|
||||||
|
|
||||||
if sign {
|
if sign {
|
||||||
@ -37,9 +37,4 @@ func Asin(x float64) float64 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Acos returns the arc cosine of x.
|
// Acos returns the arc cosine of x.
|
||||||
func Acos(x float64) float64 {
|
func Acos(x float64) float64 { return Pi/2 - Asin(x) }
|
||||||
if x > 1 || x < -1 {
|
|
||||||
return NaN()
|
|
||||||
}
|
|
||||||
return Pi/2 - Asin(x)
|
|
||||||
}
|
|
||||||
|
@ -4,7 +4,6 @@
|
|||||||
|
|
||||||
package math
|
package math
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* floating-point arctangent
|
* floating-point arctangent
|
||||||
*
|
*
|
||||||
@ -52,7 +51,7 @@ func satan(arg float64) float64 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* atan makes its argument positive and
|
* Atan makes its argument positive and
|
||||||
* calls the inner routine satan.
|
* calls the inner routine satan.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -4,13 +4,83 @@
|
|||||||
|
|
||||||
package math
|
package math
|
||||||
|
|
||||||
|
// The original C code and the long comment below are
|
||||||
/*
|
// from FreeBSD's /usr/src/lib/msun/src/e_sqrt.c and
|
||||||
* sqrt returns the square root of its floating
|
// came with this notice. The go code is a simplified
|
||||||
* point argument. Newton's method.
|
// version of the original C.
|
||||||
*
|
//
|
||||||
* calls frexp
|
// ====================================================
|
||||||
*/
|
// 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_sqrt(x)
|
||||||
|
// Return correctly rounded sqrt.
|
||||||
|
// -----------------------------------------
|
||||||
|
// | Use the hardware sqrt if you have one |
|
||||||
|
// -----------------------------------------
|
||||||
|
// Method:
|
||||||
|
// Bit by bit method using integer arithmetic. (Slow, but portable)
|
||||||
|
// 1. Normalization
|
||||||
|
// Scale x to y in [1,4) with even powers of 2:
|
||||||
|
// find an integer k such that 1 <= (y=x*2^(2k)) < 4, then
|
||||||
|
// sqrt(x) = 2^k * sqrt(y)
|
||||||
|
// 2. Bit by bit computation
|
||||||
|
// Let q = sqrt(y) truncated to i bit after binary point (q = 1),
|
||||||
|
// i 0
|
||||||
|
// i+1 2
|
||||||
|
// s = 2*q , and y = 2 * ( y - q ). (1)
|
||||||
|
// i i i i
|
||||||
|
//
|
||||||
|
// To compute q from q , one checks whether
|
||||||
|
// i+1 i
|
||||||
|
//
|
||||||
|
// -(i+1) 2
|
||||||
|
// (q + 2 ) <= y. (2)
|
||||||
|
// i
|
||||||
|
// -(i+1)
|
||||||
|
// If (2) is false, then q = q ; otherwise q = q + 2 .
|
||||||
|
// i+1 i i+1 i
|
||||||
|
//
|
||||||
|
// With some algebric manipulation, it is not difficult to see
|
||||||
|
// that (2) is equivalent to
|
||||||
|
// -(i+1)
|
||||||
|
// s + 2 <= y (3)
|
||||||
|
// i i
|
||||||
|
//
|
||||||
|
// The advantage of (3) is that s and y can be computed by
|
||||||
|
// i i
|
||||||
|
// the following recurrence formula:
|
||||||
|
// if (3) is false
|
||||||
|
//
|
||||||
|
// s = s , y = y ; (4)
|
||||||
|
// i+1 i i+1 i
|
||||||
|
//
|
||||||
|
// otherwise,
|
||||||
|
// -i -(i+1)
|
||||||
|
// s = s + 2 , y = y - s - 2 (5)
|
||||||
|
// i+1 i i+1 i i
|
||||||
|
//
|
||||||
|
// One may easily use induction to prove (4) and (5).
|
||||||
|
// Note. Since the left hand side of (3) contain only i+2 bits,
|
||||||
|
// it does not necessary to do a full (53-bit) comparison
|
||||||
|
// in (3).
|
||||||
|
// 3. Final rounding
|
||||||
|
// After generating the 53 bits result, we compute one more bit.
|
||||||
|
// Together with the remainder, we can decide whether the
|
||||||
|
// result is exact, bigger than 1/2ulp, or less than 1/2ulp
|
||||||
|
// (it will never equal to 1/2ulp).
|
||||||
|
// The rounding mode can be detected by checking whether
|
||||||
|
// huge + tiny is equal to huge, and whether huge - tiny is
|
||||||
|
// equal to huge for some floating point number "huge" and "tiny".
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Notes: Rounding mode detection omitted. The constants "mask", "shift",
|
||||||
|
// and "bias" are found in src/pkg/math/bits.go
|
||||||
|
|
||||||
// Sqrt returns the square root of x.
|
// Sqrt returns the square root of x.
|
||||||
//
|
//
|
||||||
@ -18,48 +88,55 @@ package math
|
|||||||
// Sqrt(+Inf) = +Inf
|
// Sqrt(+Inf) = +Inf
|
||||||
// Sqrt(0) = 0
|
// Sqrt(0) = 0
|
||||||
// Sqrt(x < 0) = NaN
|
// Sqrt(x < 0) = NaN
|
||||||
|
// Sqrt(NaN) = NaN
|
||||||
func Sqrt(x float64) float64 {
|
func Sqrt(x float64) float64 {
|
||||||
if IsInf(x, 1) {
|
// special cases
|
||||||
|
// TODO(rsc): Remove manual inlining of IsNaN, IsInf
|
||||||
|
// when compiler does it for us
|
||||||
|
switch {
|
||||||
|
case x != x || x > MaxFloat64: // IsNaN(x) || IsInf(x, 1):
|
||||||
return x
|
return x
|
||||||
}
|
case x == 0:
|
||||||
|
return 0
|
||||||
if x <= 0 {
|
case x < 0:
|
||||||
if x < 0 {
|
|
||||||
return NaN()
|
return NaN()
|
||||||
}
|
}
|
||||||
return 0
|
ix := Float64bits(x)
|
||||||
|
// normalize x
|
||||||
|
exp := int((ix >> shift) & mask)
|
||||||
|
if exp == 0 { // subnormal x
|
||||||
|
for ix&1<<shift == 0 {
|
||||||
|
ix <<= 1
|
||||||
|
exp--
|
||||||
}
|
}
|
||||||
|
exp++
|
||||||
y, exp := Frexp(x)
|
|
||||||
for y < 0.5 {
|
|
||||||
y = y * 2
|
|
||||||
exp = exp - 1
|
|
||||||
}
|
}
|
||||||
|
exp -= bias + 1 // unbias exponent
|
||||||
if exp&1 != 0 {
|
ix &^= mask << shift
|
||||||
y = y * 2
|
ix |= 1 << shift
|
||||||
exp = exp - 1
|
if exp&1 == 1 { // odd exp, double x to make it even
|
||||||
|
ix <<= 1
|
||||||
}
|
}
|
||||||
temp := 0.5 * (1 + y)
|
exp >>= 1 // exp = exp/2, exponent of square root
|
||||||
|
// generate sqrt(x) bit by bit
|
||||||
for exp > 60 {
|
ix <<= 1
|
||||||
temp = temp * float64(1<<30)
|
var q, s uint64 // q = sqrt(x)
|
||||||
exp = exp - 60
|
r := uint64(1 << (shift + 1)) // r = moving bit from MSB to LSB
|
||||||
|
for r != 0 {
|
||||||
|
t := s + r
|
||||||
|
if t <= ix {
|
||||||
|
s = t + r
|
||||||
|
ix -= t
|
||||||
|
q += r
|
||||||
}
|
}
|
||||||
for exp < -60 {
|
ix <<= 1
|
||||||
temp = temp / float64(1<<30)
|
r >>= 1
|
||||||
exp = exp + 60
|
|
||||||
}
|
}
|
||||||
if exp >= 0 {
|
// final rounding
|
||||||
exp = 1 << uint(exp/2)
|
if ix != 0 { // remainder, result not exact
|
||||||
temp = temp * float64(exp)
|
q += q & 1 // round according to extra bit
|
||||||
} else {
|
|
||||||
exp = 1 << uint(-exp/2)
|
|
||||||
temp = temp / float64(exp)
|
|
||||||
}
|
}
|
||||||
|
ix = q>>1 + 0x3fe0000000000000 // q/2 + 0.5
|
||||||
for i := 0; i <= 4; i++ {
|
ix += uint64(exp) << shift
|
||||||
temp = 0.5 * (temp + x/temp)
|
return Float64frombits(ix)
|
||||||
}
|
|
||||||
return temp
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user