diff options
Diffstat (limited to 'libm/double/mod2pi.c')
-rw-r--r-- | libm/double/mod2pi.c | 122 |
1 files changed, 122 insertions, 0 deletions
diff --git a/libm/double/mod2pi.c b/libm/double/mod2pi.c new file mode 100644 index 000000000..057954a9b --- /dev/null +++ b/libm/double/mod2pi.c @@ -0,0 +1,122 @@ +/* Program to test range reduction of trigonometry functions + * + * -- Steve Moshier + */ + +#include <math.h> +#ifdef ANSIPROT +extern double floor ( double ); +extern double ldexp ( double, int ); +extern double sin ( double ); +#else +double floor(), ldexp(), sin(); +#endif + +#define TPI 6.283185307179586476925 + +main() +{ +char s[40]; +double a, n, t, x, y, z; +int lflg; + +x = TPI/4.0; +t = 1.0; + +loop: + +t = 2.0 * t; + +/* Stop testing at a point beyond which the integer part of + * x/2pi cannot be represented exactly by a double precision number. + * The library trigonometry functions will probably give up long before + * this point is reached. + */ +if( t > 1.0e16 ) + exit(0); + +/* Adjust the following to choose a nontrivial x + * where test function(x) has a slope of about 1 or more. + */ +x = TPI * t + 0.5; + +z = x; +lflg = 0; + +inlup: + +/* floor() returns the largest integer less than its argument. + * If you do not have this, or AINT(), then you may convert x/TPI + * to a long integer and then back to double; but in that case + * x will be limited to the largest value that will fit into a + * long integer. + */ +n = floor( z/TPI ); + +/* Carefully subtract 2 pi n from x. + * This is done by subtracting n * 2**k in such a way that there + * is no arithmetic cancellation error at any step. The k are the + * bits in the number 2 pi. + * + * If you do not have ldexp(), then you may multiply or + * divide n by an appropriate power of 2 after each step. + * For example: + * a = z - 4*n; + * a -= 2*n; + * n /= 4; + * a -= n; n/4 + * n /= 8; + * a -= n; n/32 + * etc. + * This will only work if division by a power of 2 is exact. + */ + +a = z - ldexp(n, 2); /* 4n */ +a -= ldexp( n, 1); /* 2n */ +a -= ldexp( n, -2 ); /* n/4 */ +a -= ldexp( n, -5 ); /* n/32 */ +a -= ldexp( n, -9 ); /* n/512 */ +a += ldexp( n, -15 ); /* add n/32768 */ +a -= ldexp( n, -17 ); /* n/131072 */ +a -= ldexp( n, -18 ); +a -= ldexp( n, -20 ); +a -= ldexp( n, -22 ); +a -= ldexp( n, -24 ); +a -= ldexp( n, -28 ); +a -= ldexp( n, -32 ); +a -= ldexp( n, -37 ); +a -= ldexp( n, -39 ); +a -= ldexp( n, -40 ); +a -= ldexp( n, -42 ); +a -= ldexp( n, -46 ); +a -= ldexp( n, -47 ); + +/* Subtract what is left of 2 pi n after all the above reductions. + */ +a -= 2.44929359829470635445e-16 * n; + +/* If the test is extended too far, it is possible + * to have chosen the wrong value of n. The following + * will fix that, but at some reduction in accuracy. + */ +if( (a > TPI) || (a < -1e-11) ) + { + z = a; + lflg += 1; + printf( "Warning! Reduction failed on first try.\n" ); + goto inlup; + } +if( a < 0.0 ) + { + printf( "Warning! Reduced value < 0\n" ); + a += TPI; + } + +/* Compute the test function at x and at a = x mod 2 pi. + */ +y = sin(x); +z = sin(a); +printf( "sin(%.15e) error = %.3e\n", x, y-z ); +goto loop; +} + |