gitee.com/quant1x/num@v0.3.2/math32/sincos.go (about)

     1  // Copyright 2011 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package math32
     6  
     7  import "math/bits"
     8  
     9  /*
    10  	Floating-point sine and cosine.
    11  */
    12  
    13  // The original C code, the long comment, and the constants
    14  // below were from http://netlib.sandia.gov/cephes/cmath/sin.c,
    15  // available from http://www.netlib.org/cephes/cmath.tgz.
    16  // The go code is a simplified version of the original C.
    17  //
    18  //      sin.c
    19  //
    20  //      Circular sine
    21  //
    22  // SYNOPSIS:
    23  //
    24  // double x, y, sin();
    25  // y = sin( x );
    26  //
    27  // DESCRIPTION:
    28  //
    29  // Range reduction is into intervals of pi/4.  The reduction error is nearly
    30  // eliminated by contriving an extended precision modular arithmetic.
    31  //
    32  // Two polynomial approximating functions are employed.
    33  // Between 0 and pi/4 the sine is approximated by
    34  //      x  +  x**3 P(x**2).
    35  // Between pi/4 and pi/2 the cosine is represented as
    36  //      1  -  x**2 Q(x**2).
    37  //
    38  // ACCURACY:
    39  //
    40  //                      Relative error:
    41  // arithmetic   domain      # trials      peak         rms
    42  //    DEC       0, 10       150000       3.0e-17     7.8e-18
    43  //    IEEE -1.07e9,+1.07e9  130000       2.1e-16     5.4e-17
    44  //
    45  // Partial loss of accuracy begins to occur at x = 2**30 = 1.074e9.  The loss
    46  // is not gradual, but jumps suddenly to about 1 part in 10e7.  Results may
    47  // be meaningless for x > 2**49 = 5.6e14.
    48  //
    49  //      cos.c
    50  //
    51  //      Circular cosine
    52  //
    53  // SYNOPSIS:
    54  //
    55  // double x, y, cos();
    56  // y = cos( x );
    57  //
    58  // DESCRIPTION:
    59  //
    60  // Range reduction is into intervals of pi/4.  The reduction error is nearly
    61  // eliminated by contriving an extended precision modular arithmetic.
    62  //
    63  // Two polynomial approximating functions are employed.
    64  // Between 0 and pi/4 the cosine is approximated by
    65  //      1  -  x**2 Q(x**2).
    66  // Between pi/4 and pi/2 the sine is represented as
    67  //      x  +  x**3 P(x**2).
    68  //
    69  // ACCURACY:
    70  //
    71  //                      Relative error:
    72  // arithmetic   domain      # trials      peak         rms
    73  //    IEEE -1.07e9,+1.07e9  130000       2.1e-16     5.4e-17
    74  //    DEC        0,+1.07e9   17000       3.0e-17     7.2e-18
    75  //
    76  // Cephes Math Library Release 2.8:  June, 2000
    77  // Copyright 1984, 1987, 1989, 1992, 2000 by Stephen L. Moshier
    78  //
    79  // The readme file at http://netlib.sandia.gov/cephes/ says:
    80  //    Some software in this archive may be from the book _Methods and
    81  // Programs for Mathematical Functions_ (Prentice-Hall or Simon & Schuster
    82  // International, 1989) or from the Cephes Mathematical Library, a
    83  // commercial product. In either event, it is copyrighted by the author.
    84  // What you see here may be used freely but it comes with no support or
    85  // guarantee.
    86  //
    87  //   The two known misprints in the book are repaired here in the
    88  // source listings for the gamma function and the incomplete beta
    89  // integral.
    90  //
    91  //   Stephen L. Moshier
    92  //   moshier@na-net.ornl.gov
    93  
    94  // sin coefficients
    95  var _sin = [...]float32{
    96  	1.58962301576546568060e-10, // 0x3de5d8fd1fd19ccd
    97  	-2.50507477628578072866e-8, // 0xbe5ae5e5a9291f5d
    98  	2.75573136213857245213e-6,  // 0x3ec71de3567d48a1
    99  	-1.98412698295895385996e-4, // 0xbf2a01a019bfdf03
   100  	8.33333333332211858878e-3,  // 0x3f8111111110f7d0
   101  	-1.66666666666666307295e-1, // 0xbfc5555555555548
   102  }
   103  
   104  // cos coefficients
   105  var _cos = [...]float32{
   106  	-1.13585365213876817300e-11, // 0xbda8fa49a0861a9b
   107  	2.08757008419747316778e-9,   // 0x3e21ee9d7b4e3f05
   108  	-2.75573141792967388112e-7,  // 0xbe927e4f7eac4bc6
   109  	2.48015872888517045348e-5,   // 0x3efa01a019c844f5
   110  	-1.38888888888730564116e-3,  // 0xbf56c16c16c14f91
   111  	4.16666666666665929218e-2,   // 0x3fa555555555554b
   112  }
   113  
   114  // Sincos returns Sin(x), Cos(x).
   115  //
   116  // Special cases are:
   117  //
   118  //	Sincos(±0) = ±0, 1
   119  //	Sincos(±Inf) = NaN, NaN
   120  //	Sincos(NaN) = NaN, NaN
   121  func Sincos(x float32) (sin, cos float32) {
   122  	const (
   123  		PI4A = 7.85398125648498535156e-1  // 0x3fe921fb40000000, Pi/4 split into three parts
   124  		PI4B = 3.77489470793079817668e-8  // 0x3e64442d00000000,
   125  		PI4C = 2.69515142907905952645e-15 // 0x3ce8469898cc5170,
   126  	)
   127  	// special cases
   128  	switch {
   129  	case x == 0:
   130  		return x, 1 // return ±0.0, 1.0
   131  	case IsNaN(x) || IsInf(x, 0):
   132  		return NaN(), NaN()
   133  	}
   134  
   135  	// make argument positive
   136  	sinSign, cosSign := false, false
   137  	if x < 0 {
   138  		x = -x
   139  		sinSign = true
   140  	}
   141  
   142  	var j uint64
   143  	var y, z float32
   144  	if x >= reduceThreshold {
   145  		j, z = trigReduce(x)
   146  	} else {
   147  		j = uint64(x * (4 / Pi)) // integer part of x/(Pi/4), as integer for tests on the phase angle
   148  		y = float32(j)           // integer part of x/(Pi/4), as float
   149  
   150  		if j&1 == 1 { // map zeros to origin
   151  			j++
   152  			y++
   153  		}
   154  		j &= 7                               // octant modulo 2Pi radians (360 degrees)
   155  		z = ((x - y*PI4A) - y*PI4B) - y*PI4C // Extended precision modular arithmetic
   156  	}
   157  	if j > 3 { // reflect in x axis
   158  		j -= 4
   159  		sinSign, cosSign = !sinSign, !cosSign
   160  	}
   161  	if j > 1 {
   162  		cosSign = !cosSign
   163  	}
   164  
   165  	zz := z * z
   166  	cos = 1.0 - 0.5*zz + zz*zz*((((((_cos[0]*zz)+_cos[1])*zz+_cos[2])*zz+_cos[3])*zz+_cos[4])*zz+_cos[5])
   167  	sin = z + z*zz*((((((_sin[0]*zz)+_sin[1])*zz+_sin[2])*zz+_sin[3])*zz+_sin[4])*zz+_sin[5])
   168  	if j == 1 || j == 2 {
   169  		sin, cos = cos, sin
   170  	}
   171  	if cosSign {
   172  		cos = -cos
   173  	}
   174  	if sinSign {
   175  		sin = -sin
   176  	}
   177  	return
   178  }
   179  
   180  // Sin returns the sine of the radian argument x.
   181  //
   182  // Special cases are:
   183  //
   184  //	Sin(±0) = ±0
   185  //	Sin(±Inf) = NaN
   186  //	Sin(NaN) = NaN
   187  func Sin(x float32) float32 {
   188  	const (
   189  		PI4A = 7.85398125648498535156e-1  // 0x3fe921fb40000000, Pi/4 split into three parts
   190  		PI4B = 3.77489470793079817668e-8  // 0x3e64442d00000000,
   191  		PI4C = 2.69515142907905952645e-15 // 0x3ce8469898cc5170,
   192  	)
   193  	// special cases
   194  	switch {
   195  	case x == 0 || IsNaN(x):
   196  		return x // return ±0 || NaN()
   197  	case IsInf(x, 0):
   198  		return NaN()
   199  	}
   200  
   201  	// make argument positive but save the sign
   202  	sign := false
   203  	if x < 0 {
   204  		x = -x
   205  		sign = true
   206  	}
   207  
   208  	var j uint64
   209  	var y, z float32
   210  	if x >= reduceThreshold {
   211  		j, z = trigReduce(x)
   212  	} else {
   213  		j = uint64(x * (4 / Pi)) // integer part of x/(Pi/4), as integer for tests on the phase angle
   214  		y = float32(j)           // integer part of x/(Pi/4), as float
   215  
   216  		// map zeros to origin
   217  		if j&1 == 1 {
   218  			j++
   219  			y++
   220  		}
   221  		j &= 7                               // octant modulo 2Pi radians (360 degrees)
   222  		z = ((x - y*PI4A) - y*PI4B) - y*PI4C // Extended precision modular arithmetic
   223  	}
   224  	// reflect in x axis
   225  	if j > 3 {
   226  		sign = !sign
   227  		j -= 4
   228  	}
   229  	zz := z * z
   230  	if j == 1 || j == 2 {
   231  		y = 1.0 - 0.5*zz + zz*zz*((((((_cos[0]*zz)+_cos[1])*zz+_cos[2])*zz+_cos[3])*zz+_cos[4])*zz+_cos[5])
   232  	} else {
   233  		y = z + z*zz*((((((_sin[0]*zz)+_sin[1])*zz+_sin[2])*zz+_sin[3])*zz+_sin[4])*zz+_sin[5])
   234  	}
   235  	if sign {
   236  		y = -y
   237  	}
   238  	return y
   239  }
   240  
   241  // Cos returns the cosine of the radian argument x.
   242  //
   243  // Special cases are:
   244  //
   245  //	Cos(±Inf) = NaN
   246  //	Cos(NaN) = NaN
   247  func Cos(x float32) float32 {
   248  	const (
   249  		PI4A = 7.85398125648498535156e-1  // 0x3fe921fb40000000, Pi/4 split into three parts
   250  		PI4B = 3.77489470793079817668e-8  // 0x3e64442d00000000,
   251  		PI4C = 2.69515142907905952645e-15 // 0x3ce8469898cc5170,
   252  	)
   253  	// special cases
   254  	switch {
   255  	case IsNaN(x) || IsInf(x, 0):
   256  		return NaN()
   257  	}
   258  
   259  	// make argument positive
   260  	sign := false
   261  	x = Abs(x)
   262  
   263  	var j uint64
   264  	var y, z float32
   265  	if x >= reduceThreshold {
   266  		j, z = trigReduce(x)
   267  	} else {
   268  		j = uint64(x * (4 / Pi)) // integer part of x/(Pi/4), as integer for tests on the phase angle
   269  		y = float32(j)           // integer part of x/(Pi/4), as float
   270  
   271  		// map zeros to origin
   272  		if j&1 == 1 {
   273  			j++
   274  			y++
   275  		}
   276  		j &= 7                               // octant modulo 2Pi radians (360 degrees)
   277  		z = ((x - y*PI4A) - y*PI4B) - y*PI4C // Extended precision modular arithmetic
   278  	}
   279  
   280  	if j > 3 {
   281  		j -= 4
   282  		sign = !sign
   283  	}
   284  	if j > 1 {
   285  		sign = !sign
   286  	}
   287  
   288  	zz := z * z
   289  	if j == 1 || j == 2 {
   290  		y = z + z*zz*((((((_sin[0]*zz)+_sin[1])*zz+_sin[2])*zz+_sin[3])*zz+_sin[4])*zz+_sin[5])
   291  	} else {
   292  		y = 1.0 - 0.5*zz + zz*zz*((((((_cos[0]*zz)+_cos[1])*zz+_cos[2])*zz+_cos[3])*zz+_cos[4])*zz+_cos[5])
   293  	}
   294  	if sign {
   295  		y = -y
   296  	}
   297  	return y
   298  }
   299  
   300  // reduceThreshold is the maximum value of x where the reduction using Pi/4
   301  // in 3 float64 parts still gives accurate results. This threshold
   302  // is set by y*C being representable as a float64 without error
   303  // where y is given by y = floor(x * (4 / Pi)) and C is the leading partial
   304  // terms of 4/Pi. Since the leading terms (PI4A and PI4B in sin.go) have 30
   305  // and 32 trailing zero bits, y should have less than 30 significant bits.
   306  //
   307  //	y < 1<<30  -> floor(x*4/Pi) < 1<<30 -> x < (1<<30 - 1) * Pi/4
   308  //
   309  // So, conservatively we can take x < 1<<29.
   310  // Above this threshold Payne-Hanek range reduction must be used.
   311  const reduceThreshold = 1 << 29
   312  
   313  // trigReduce implements Payne-Hanek range reduction by Pi/4
   314  // for x > 0. It returns the integer part mod 8 (j) and
   315  // the fractional part (z) of x / (Pi/4).
   316  // The implementation is based on:
   317  // "ARGUMENT REDUCTION FOR HUGE ARGUMENTS: Good to the Last Bit"
   318  // K. C. Ng et al, March 24, 1992
   319  // The simulated multi-precision calculation of x*B uses 64-bit integer arithmetic.
   320  func trigReduce(x float32) (j uint64, z float32) {
   321  	const PI4 = Pi / 4
   322  	if x < PI4 {
   323  		return 0, x
   324  	}
   325  	// Extract out the integer and exponent such that,
   326  	// x = ix * 2 ** exp.
   327  	ix := Float32bits(x)
   328  	exp := int(ix>>shift&mask) - bias - shift
   329  	ix &^= mask << shift
   330  	ix |= 1 << shift
   331  	// Use the exponent to extract the 3 appropriate uint64 digits from mPi4,
   332  	// B ~ (z0, z1, z2), such that the product leading digit has the exponent -61.
   333  	// Note, exp >= -53 since x >= PI4 and exp < 971 for maximum float64.
   334  	const floatingbits = 32 - 3
   335  	digit, bitshift := uint(exp+floatingbits)/32, uint(exp+floatingbits)%32
   336  	z0 := (mPi4[digit] << bitshift) | (mPi4[digit+1] >> (32 - bitshift))
   337  	z1 := (mPi4[digit+1] << bitshift) | (mPi4[digit+2] >> (32 - bitshift))
   338  	z2 := (mPi4[digit+2] << bitshift) | (mPi4[digit+3] >> (32 - bitshift))
   339  	// Multiply mantissa by the digits and extract the upper two digits (hi, lo).
   340  	z2hi, _ := bits.Mul64(z2, uint64(ix))
   341  	z1hi, z1lo := bits.Mul64(z1, uint64(ix))
   342  	z0lo := z0 * uint64(ix)
   343  	lo, c := bits.Add64(z1lo, z2hi, 0)
   344  	hi, _ := bits.Add64(z0lo, z1hi, c)
   345  	// The top 3 bits are j.
   346  	j = hi >> floatingbits
   347  	// Extract the fraction and find its magnitude.
   348  	hi = hi<<3 | lo>>floatingbits
   349  	lz := uint(bits.LeadingZeros64(hi))
   350  	e := uint64(bias - (lz + 1))
   351  	// Clear implicit mantissa bit and shift into place.
   352  	hi = (hi << (lz + 1)) | (lo >> (32 - (lz + 1)))
   353  	hi >>= 43 - shift
   354  	// Include the exponent and convert to a float.
   355  	hi |= e << shift
   356  	z = Float32frombits(uint32(hi))
   357  	// Map zeros to origin.
   358  	if j&1 == 1 {
   359  		j++
   360  		j &= 7
   361  		z--
   362  	}
   363  	// Multiply the fractional part by pi/4.
   364  	return j, z * PI4
   365  }
   366  
   367  // mPi4 is the binary digits of 4/pi as a uint64 array,
   368  // that is, 4/pi = Sum mPi4[i]*2^(-64*i)
   369  // 19 64-bit digits and the leading one bit give 1217 bits
   370  // of precision to handle the largest possible float64 exponent.
   371  var mPi4 = [...]uint64{
   372  	0x0000000000000001,
   373  	0x45f306dc9c882a53,
   374  	0xf84eafa3ea69bb81,
   375  	0xb6c52b3278872083,
   376  	0xfca2c757bd778ac3,
   377  	0x6e48dc74849ba5c0,
   378  	0x0c925dd413a32439,
   379  	0xfc3bd63962534e7d,
   380  	0xd1046bea5d768909,
   381  	0xd338e04d68befc82,
   382  	0x7323ac7306a673e9,
   383  	0x3908bf177bf25076,
   384  	0x3ff12fffbc0b301f,
   385  	0xde5e2316b414da3e,
   386  	0xda6cfd9e4f96136e,
   387  	0x9e8c7ecd3cbfd45a,
   388  	0xea4f758fd7cbe2f6,
   389  	0x7a0e73ef14a525d4,
   390  	0xd7f6bf623f1aba10,
   391  	0xac06608df8f6d757,
   392  }