rtt-f030/components/external/espruino/libs/math/mod2pi.c

123 lines
2.7 KiB
C
Raw Normal View History

2014-02-25 01:47:49 +08:00
/* Program to test range reduction of trigonometry functions
*
* -- Steve Moshier
*/
#include "mconf.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;
}