xref: /reactos/sdk/lib/crt/math/libm_sse2/tan.c (revision 0b366ea1)
1 
2 /*******************************************************************************
3 MIT License
4 -----------
5 
6 Copyright (c) 2002-2019 Advanced Micro Devices, Inc.
7 
8 Permission is hereby granted, free of charge, to any person obtaining a copy
9 of this Software and associated documentaon files (the "Software"), to deal
10 in the Software without restriction, including without limitation the rights
11 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 copies of the Software, and to permit persons to whom the Software is
13 furnished to do so, subject to the following conditions:
14 
15 The above copyright notice and this permission notice shall be included in
16 all copies or substantial portions of the Software.
17 
18 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24 THE SOFTWARE.
25 *******************************************************************************/
26 
27 #include "libm.h"
28 #include "libm_util.h"
29 
30 #define USE_NAN_WITH_FLAGS
31 #define USE_VAL_WITH_FLAGS
32 #define USE_HANDLE_ERROR
33 #include "libm_inlines.h"
34 #undef USE_NAN_WITH_FLAGS
35 #undef USE_VAL_WITH_FLAGS
36 #undef USE_HANDLE_ERROR
37 
38 #include "libm_errno.h"
39 
40 /* tan(x + xx) approximation valid on the interval [-pi/4,pi/4].
41    If recip is true return -1/tan(x + xx) instead. */
42 static inline double tan_piby4(double x, double xx, int recip)
43 {
44   double r, t1, t2, xl;
45   int transform = 0;
46   static const double
47      piby4_lead = 7.85398163397448278999e-01, /* 0x3fe921fb54442d18 */
48      piby4_tail = 3.06161699786838240164e-17; /* 0x3c81a62633145c06 */
49 
50   /* In order to maintain relative precision transform using the identity:
51      tan(pi/4-x) = (1-tan(x))/(1+tan(x)) for arguments close to pi/4.
52      Similarly use tan(x-pi/4) = (tan(x)-1)/(tan(x)+1) close to -pi/4. */
53 
54   if (x > 0.68)
55     {
56       transform = 1;
57       x = piby4_lead - x;
58       xl = piby4_tail - xx;
59       x += xl;
60       xx = 0.0;
61     }
62   else if (x < -0.68)
63     {
64       transform = -1;
65       x = piby4_lead + x;
66       xl = piby4_tail + xx;
67       x += xl;
68       xx = 0.0;
69     }
70 
71   /* Core Remez [2,3] approximation to tan(x+xx) on the
72      interval [0,0.68]. */
73 
74   r = x*x + 2.0 * x * xx;
75   t1 = x;
76   t2 = xx + x*r*
77     (0.372379159759792203640806338901e0 +
78      (-0.229345080057565662883358588111e-1 +
79       0.224044448537022097264602535574e-3*r)*r)/
80     (0.111713747927937668539901657944e1 +
81      (-0.515658515729031149329237816945e0 +
82       (0.260656620398645407524064091208e-1 -
83        0.232371494088563558304549252913e-3*r)*r)*r);
84 
85   /* Reconstruct tan(x) in the transformed case. */
86 
87   if (transform)
88     {
89       double t;
90       t = t1 + t2;
91       if (recip)
92          return transform*(2*t/(t-1) - 1.0);
93       else
94          return transform*(1.0 - 2*t/(1+t));
95     }
96 
97   if (recip)
98     {
99       /* Compute -1.0/(t1 + t2) accurately */
100       double trec, trec_top, z1, z2, t;
101       unsigned long long u;
102       t = t1 + t2;
103       GET_BITS_DP64(t, u);
104       u &= 0xffffffff00000000;
105       PUT_BITS_DP64(u, z1);
106       z2 = t2 - (z1 - t1);
107       trec = -1.0 / t;
108       GET_BITS_DP64(trec, u);
109       u &= 0xffffffff00000000;
110       PUT_BITS_DP64(u, trec_top);
111       return trec_top + trec * ((1.0 + trec_top * z1) + trec_top * z2);
112 
113     }
114   else
115     return t1 + t2;
116 }
117 
118 #ifdef _MSC_VER
119 #pragma function(tan)
120 #endif
121 
122 double tan(double x)
123 {
124   double r, rr;
125   int region, xneg;
126 
127   unsigned long long ux, ax;
128   GET_BITS_DP64(x, ux);
129   ax = (ux & ~SIGNBIT_DP64);
130   if (ax <= 0x3fe921fb54442d18) /* abs(x) <= pi/4 */
131     {
132       if (ax < 0x3f20000000000000) /* abs(x) < 2.0^(-13) */
133         {
134           if (ax < 0x3e40000000000000) /* abs(x) < 2.0^(-27) */
135 	    {
136 	      if (ax == 0x0000000000000000) return x;
137               else return val_with_flags(x, AMD_F_INEXACT);
138 	    }
139           else
140             {
141               /* Using a temporary variable prevents 64-bit VC++ from
142                  rearranging
143                     x + x*x*x*0.333333333333333333;
144                  into
145                     x * (1 + x*x*0.333333333333333333);
146                  The latter results in an incorrectly rounded answer. */
147               double tmp;
148               tmp = x*x*x*0.333333333333333333;
149               return x + tmp;
150             }
151         }
152       else
153         return tan_piby4(x, 0.0, 0);
154     }
155   else if ((ux & EXPBITS_DP64) == EXPBITS_DP64)
156     {
157       /* x is either NaN or infinity */
158       if (ux & MANTBITS_DP64)
159         /* x is NaN */
160         return _handle_error("tan", OP_TAN, ux|0x0008000000000000, _DOMAIN, 0,
161                             EDOM, x, 0.0, 1);
162       else
163         /* x is infinity. Return a NaN */
164         return _handle_error("tan", OP_TAN, INDEFBITPATT_DP64, _DOMAIN, AMD_F_INVALID,
165                             EDOM, x, 0.0, 1);
166     }
167   xneg = (ax != ux);
168 
169 
170   if (xneg)
171     x = -x;
172 
173   if (x < 5.0e5)
174     {
175       /* For these size arguments we can just carefully subtract the
176          appropriate multiple of pi/2, using extra precision where
177          x is close to an exact multiple of pi/2 */
178       static const double
179         twobypi =  6.36619772367581382433e-01, /* 0x3fe45f306dc9c883 */
180         piby2_1  =  1.57079632673412561417e+00, /* 0x3ff921fb54400000 */
181         piby2_1tail =  6.07710050650619224932e-11, /* 0x3dd0b4611a626331 */
182         piby2_2  =  6.07710050630396597660e-11, /* 0x3dd0b4611a600000 */
183         piby2_2tail =  2.02226624879595063154e-21, /* 0x3ba3198a2e037073 */
184         piby2_3  =  2.02226624871116645580e-21, /* 0x3ba3198a2e000000 */
185         piby2_3tail =  8.47842766036889956997e-32; /* 0x397b839a252049c1 */
186       double t, rhead, rtail;
187       int npi2;
188       unsigned long long uy, xexp, expdiff;
189       xexp  = ax >> EXPSHIFTBITS_DP64;
190       /* How many pi/2 is x a multiple of? */
191       if (ax <= 0x400f6a7a2955385e) /* 5pi/4 */
192         {
193           if (ax <= 0x4002d97c7f3321d2) /* 3pi/4 */
194             npi2 = 1;
195           else
196             npi2 = 2;
197         }
198       else if (ax <= 0x401c463abeccb2bb) /* 9pi/4 */
199         {
200           if (ax <= 0x4015fdbbe9bba775) /* 7pi/4 */
201             npi2 = 3;
202           else
203             npi2 = 4;
204         }
205       else
206         npi2  = (int)(x * twobypi + 0.5);
207       /* Subtract the multiple from x to get an extra-precision remainder */
208       rhead  = x - npi2 * piby2_1;
209       rtail  = npi2 * piby2_1tail;
210       GET_BITS_DP64(rhead, uy);
211       expdiff = xexp - ((uy & EXPBITS_DP64) >> EXPSHIFTBITS_DP64);
212       if (expdiff > 15)
213         {
214           /* The remainder is pretty small compared with x, which
215              implies that x is a near multiple of pi/2
216              (x matches the multiple to at least 15 bits) */
217           t  = rhead;
218           rtail  = npi2 * piby2_2;
219           rhead  = t - rtail;
220           rtail  = npi2 * piby2_2tail - ((t - rhead) - rtail);
221           if (expdiff > 48)
222             {
223               /* x matches a pi/2 multiple to at least 48 bits */
224               t  = rhead;
225               rtail  = npi2 * piby2_3;
226               rhead  = t - rtail;
227               rtail  = npi2 * piby2_3tail - ((t - rhead) - rtail);
228             }
229         }
230       r = rhead - rtail;
231       rr = (rhead - r) - rtail;
232       region = npi2 & 3;
233     }
234   else
235     {
236       /* Reduce x into range [-pi/4,pi/4] */
237       __remainder_piby2(x, &r, &rr, &region);
238     }
239 
240   if (xneg)
241     return -tan_piby4(r, rr, region & 1);
242   else
243     return tan_piby4(r, rr, region & 1);
244 }
245