* use unsigned arithmetics on the representation * store arg reduction quotient in unsigned (so n%2 would work like n&1) * use different convention to pass the arg reduction bit to __tan (this argument used to be 1 for even and -1 for odd reduction which meant obscure bithacks, the new n&1 is cleaner) * raise inexact and underflow flags correctly for small x (tanl(x) may still raise spurious underflow for small but normal x) (this exception raising code increases codesize a bit, similar fixes are needed in many other places, it may worth investigating at some point if the inexact and underflow flags are worth raising correctly as this is not strictly required by the standard) * tanf manual reduction optimization is kept for now * tanl code path is cleaned up to follow similar logic to tan and tanf
70 lines
1.9 KiB
C
70 lines
1.9 KiB
C
/* origin: FreeBSD /usr/src/lib/msun/src/s_tan.c */
|
|
/*
|
|
* ====================================================
|
|
* 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.
|
|
* ====================================================
|
|
*/
|
|
/* tan(x)
|
|
* Return tangent function of x.
|
|
*
|
|
* kernel function:
|
|
* __tan ... tangent function on [-pi/4,pi/4]
|
|
* __rem_pio2 ... argument reduction routine
|
|
*
|
|
* Method.
|
|
* Let S,C and T denote the sin, cos and tan respectively on
|
|
* [-PI/4, +PI/4]. Reduce the argument x to y1+y2 = x-k*pi/2
|
|
* in [-pi/4 , +pi/4], and let n = k mod 4.
|
|
* We have
|
|
*
|
|
* n sin(x) cos(x) tan(x)
|
|
* ----------------------------------------------------------
|
|
* 0 S C T
|
|
* 1 C -S -1/T
|
|
* 2 -S -C T
|
|
* 3 -C S -1/T
|
|
* ----------------------------------------------------------
|
|
*
|
|
* Special cases:
|
|
* Let trig be any of sin, cos, or tan.
|
|
* trig(+-INF) is NaN, with signals;
|
|
* trig(NaN) is that NaN;
|
|
*
|
|
* Accuracy:
|
|
* TRIG(x) returns trig(x) nearly rounded
|
|
*/
|
|
|
|
#include "libm.h"
|
|
|
|
double tan(double x)
|
|
{
|
|
double y[2];
|
|
uint32_t ix;
|
|
unsigned n;
|
|
|
|
GET_HIGH_WORD(ix, x);
|
|
ix &= 0x7fffffff;
|
|
|
|
/* |x| ~< pi/4 */
|
|
if (ix <= 0x3fe921fb) {
|
|
if (ix < 0x3e400000) { /* |x| < 2**-27 */
|
|
/* raise inexact if x!=0 and underflow if subnormal */
|
|
FORCE_EVAL(ix < 0x00100000 ? x/0x1p120f : x+0x1p120f);
|
|
return x;
|
|
}
|
|
return __tan(x, 0.0, 0);
|
|
}
|
|
|
|
/* tan(Inf or NaN) is NaN */
|
|
if (ix >= 0x7ff00000)
|
|
return x - x;
|
|
|
|
/* argument reduction */
|
|
n = __rem_pio2(x, y);
|
|
return __tan(y[0], y[1], n&1);
|
|
}
|