| 1 | // Ported from musl, which is licensed under the MIT license: |
| 2 | // https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT |
| 3 | // |
| 4 | // https://git.musl-libc.org/cgit/musl/tree/src/math/__cos.c |
| 5 | // https://git.musl-libc.org/cgit/musl/tree/src/math/__cosdf.c |
| 6 | // https://git.musl-libc.org/cgit/musl/tree/src/math/__sin.c |
| 7 | // https://git.musl-libc.org/cgit/musl/tree/src/math/__sindf.c |
| 8 | // https://git.musl-libc.org/cgit/musl/tree/src/math/__tand.c |
| 9 | // https://git.musl-libc.org/cgit/musl/tree/src/math/__tandf.c |
| 10 | // https://git.musl-libc.org/cgit/musl/tree/src/math/__sinl.c |
| 11 | // https://git.musl-libc.org/cgit/musl/tree/src/math/__cosl.c |
| 12 | // https://git.musl-libc.org/cgit/musl/tree/src/math/__tanl.c |
| 13 | |
| 14 | const std = @import("std"); |
| 15 | |
| 16 | pub const pi_4 = std.math.pi / 4.0; |
| 17 | |
| 18 | /// kernel cos function on [-pi/4, pi/4], pi/4 ~ 0.785398164 |
| 19 | /// Input x is assumed to be bounded by ~pi/4 in magnitude. |
| 20 | /// Input y is the tail of x. |
| 21 | /// |
| 22 | /// Algorithm |
| 23 | /// 1. Since cos(-x) = cos(x), we need only to consider positive x. |
| 24 | /// 2. if x < 2^-27 (hx<0x3e400000 0), return 1 with inexact if x!=0. |
| 25 | /// 3. cos(x) is approximated by a polynomial of degree 14 on |
| 26 | /// [0,pi/4] |
| 27 | /// 4 14 |
| 28 | /// cos(x) ~ 1 - x*x/2 + C1*x + ... + C6*x |
| 29 | /// where the remez error is |
| 30 | /// |
| 31 | /// | 2 4 6 8 10 12 14 | -58 |
| 32 | /// |cos(x)-(1-.5*x +C1*x +C2*x +C3*x +C4*x +C5*x +C6*x )| <= 2 |
| 33 | /// | | |
| 34 | /// |
| 35 | /// 4 6 8 10 12 14 |
| 36 | /// 4. let r = C1*x +C2*x +C3*x +C4*x +C5*x +C6*x , then |
| 37 | /// cos(x) ~ 1 - x*x/2 + r |
| 38 | /// since cos(x+y) ~ cos(x) - sin(x)*y |
| 39 | /// ~ cos(x) - x*y, |
| 40 | /// a correction term is necessary in cos(x) and hence |
| 41 | /// cos(x+y) = 1 - (x*x/2 - (r - x*y)) |
| 42 | /// For better accuracy, rearrange to |
| 43 | /// cos(x+y) ~ w + (tmp + (r-x*y)) |
| 44 | /// where w = 1 - x*x/2 and tmp is a tiny correction term |
| 45 | /// (1 - x*x/2 == w + tmp exactly in infinite precision). |
| 46 | /// The exactness of w + tmp in infinite precision depends on w |
| 47 | /// and tmp having the same precision as x. If they have extra |
| 48 | /// precision due to compiler bugs, then the extra precision is |
| 49 | /// only good provided it is retained in all terms of the final |
| 50 | /// expression for cos(). Retention happens in all cases tested |
| 51 | /// under FreeBSD, so don't pessimize things by forcibly clipping |
| 52 | /// any extra precision in w. |
| 53 | pub fn cos(x: f64, y: f64) f64 { |
| 54 | const C1 = 4.16666666666666019037e-02; // 0x3FA55555, 0x5555554C |
| 55 | const C2 = -1.38888888888741095749e-03; // 0xBF56C16C, 0x16C15177 |
| 56 | const C3 = 2.48015872894767294178e-05; // 0x3EFA01A0, 0x19CB1590 |
| 57 | const C4 = -2.75573143513906633035e-07; // 0xBE927E4F, 0x809C52AD |
| 58 | const C5 = 2.08757232129817482790e-09; // 0x3E21EE9E, 0xBDB4B1C4 |
| 59 | const C6 = -1.13596475577881948265e-11; // 0xBDA8FAE9, 0xBE8838D4 |
| 60 | |
| 61 | const z = x * x; |
| 62 | const zs = z * z; |
| 63 | const r = z * (C1 + z * (C2 + z * C3)) + zs * zs * (C4 + z * (C5 + z * C6)); |
| 64 | const hz = 0.5 * z; |
| 65 | const w = 1.0 - hz; |
| 66 | return w + (((1.0 - w) - hz) + (z * r - x * y)); |
| 67 | } |
| 68 | |
| 69 | pub fn cosdf(x: f64) f32 { |
| 70 | // |cos(x) - c(x)| < 2**-34.1 (~[-5.37e-11, 5.295e-11]). |
| 71 | const C0 = -0x1ffffffd0c5e81.0p-54; // -0.499999997251031003120 |
| 72 | const C1 = 0x155553e1053a42.0p-57; // 0.0416666233237390631894 |
| 73 | const C2 = -0x16c087e80f1e27.0p-62; // -0.00138867637746099294692 |
| 74 | const C3 = 0x199342e0ee5069.0p-68; // 0.0000243904487962774090654 |
| 75 | |
| 76 | // Try to optimize for parallel evaluation as in __tandf.c. |
| 77 | const z = x * x; |
| 78 | const w = z * z; |
| 79 | const r = C2 + z * C3; |
| 80 | return @floatCast(((1.0 + z * C0) + w * C1) + (w * z) * r); |
| 81 | } |
| 82 | |
| 83 | pub fn cosx(x: f80, y: f80) f80 { |
| 84 | const C1: f80 = 0.0416666666666666666136; |
| 85 | const C2: f64 = -0.0013888888888888874; |
| 86 | const C3: f64 = 0.000024801587301571716; |
| 87 | const C4: f64 = -0.00000027557319215507120; |
| 88 | const C5: f64 = 0.0000000020876754400407278; |
| 89 | const C6: f64 = -1.1470297442401303e-11; |
| 90 | const C7: f64 = 4.7383039476436467e-14; |
| 91 | |
| 92 | const z = x * x; |
| 93 | const r = z * (C1 + z * (C2 + z * (C3 + z * (C4 + |
| 94 | z * (C5 + z * (C6 + z * C7)))))); |
| 95 | const hz = 0.5 * z; |
| 96 | const w = 1.0 - hz; |
| 97 | |
| 98 | return w + (((1.0 - w) - hz) + (z * r - x * y)); |
| 99 | } |
| 100 | |
| 101 | pub fn cosq(x: f128, y: f128) f128 { |
| 102 | const C1: f128 = 0.04166666666666666666666666666666658424671; |
| 103 | const C2: f128 = -0.001388888888888888888888888888863490893732; |
| 104 | const C3: f128 = 0.00002480158730158730158730158600795304914210; |
| 105 | const C4: f128 = -0.2755731922398589065255474947078934284324e-6; |
| 106 | const C5: f128 = 0.2087675698786809897659225313136400793948e-8; |
| 107 | const C6: f128 = -0.1147074559772972315817149986812031204775e-10; |
| 108 | const C7: f128 = 0.4779477332386808976875457937252120293400e-13; |
| 109 | const C8: f64 = -0.1561920696721507929516718307820958119868e-15; |
| 110 | const C9: f64 = 0.4110317413744594971475941557607804508039e-18; |
| 111 | const C10: f64 = -0.8896592467191938803288521958313920156409e-21; |
| 112 | const C11: f64 = 0.1601061435794535138244346256065192782581e-23; |
| 113 | |
| 114 | const z = x * x; |
| 115 | const r = z * (C1 + z * (C2 + z * (C3 + z * (C4 + z * (C5 + z * (C6 + |
| 116 | z * (C7 + z * (C8 + z * (C9 + z * (C10 + z * C11)))))))))); |
| 117 | const hz = 0.5 * z; |
| 118 | const w = 1.0 - hz; |
| 119 | |
| 120 | return w + (((1.0 - w) - hz) + (z * r - x * y)); |
| 121 | } |
| 122 | |
| 123 | /// kernel sin function on ~[-pi/4, pi/4] (except on -0), pi/4 ~ 0.7854 |
| 124 | /// Input x is assumed to be bounded by ~pi/4 in magnitude. |
| 125 | /// Input y is the tail of x. |
| 126 | /// Input iy indicates whether y is 0. (if iy=0, y assume to be 0). |
| 127 | /// |
| 128 | /// Algorithm |
| 129 | /// 1. Since sin(-x) = -sin(x), we need only to consider positive x. |
| 130 | /// 2. Callers must return sin(-0) = -0 without calling here since our |
| 131 | /// odd polynomial is not evaluated in a way that preserves -0. |
| 132 | /// Callers may do the optimization sin(x) ~ x for tiny x. |
| 133 | /// 3. sin(x) is approximated by a polynomial of degree 13 on |
| 134 | /// [0,pi/4] |
| 135 | /// 3 13 |
| 136 | /// sin(x) ~ x + S1*x + ... + S6*x |
| 137 | /// where |
| 138 | /// |
| 139 | /// |sin(x) 2 4 6 8 10 12 | -58 |
| 140 | /// |----- - (1+S1*x +S2*x +S3*x +S4*x +S5*x +S6*x )| <= 2 |
| 141 | /// | x | |
| 142 | /// |
| 143 | /// 4. sin(x+y) = sin(x) + sin'(x')*y |
| 144 | /// ~ sin(x) + (1-x*x/2)*y |
| 145 | /// For better accuracy, let |
| 146 | /// 3 2 2 2 2 |
| 147 | /// r = x *(S2+x *(S3+x *(S4+x *(S5+x *S6)))) |
| 148 | /// then 3 2 |
| 149 | /// sin(x) = x + (S1*x + (x *(r-y/2)+y)) |
| 150 | pub fn sin(x: f64, y: f64, iy: i32) f64 { |
| 151 | const S1 = -1.66666666666666324348e-01; // 0xBFC55555, 0x55555549 |
| 152 | const S2 = 8.33333333332248946124e-03; // 0x3F811111, 0x1110F8A6 |
| 153 | const S3 = -1.98412698298579493134e-04; // 0xBF2A01A0, 0x19C161D5 |
| 154 | const S4 = 2.75573137070700676789e-06; // 0x3EC71DE3, 0x57B1FE7D |
| 155 | const S5 = -2.50507602534068634195e-08; // 0xBE5AE5E6, 0x8A2B9CEB |
| 156 | const S6 = 1.58969099521155010221e-10; // 0x3DE5D93A, 0x5ACFD57C |
| 157 | |
| 158 | const z = x * x; |
| 159 | const w = z * z; |
| 160 | const r = S2 + z * (S3 + z * S4) + z * w * (S5 + z * S6); |
| 161 | const v = z * x; |
| 162 | if (iy == 0) { |
| 163 | return x + v * (S1 + z * r); |
| 164 | } else { |
| 165 | return x - ((z * (0.5 * y - v * r) - y) - v * S1); |
| 166 | } |
| 167 | } |
| 168 | |
| 169 | pub fn sindf(x: f64) f32 { |
| 170 | // |sin(x)/x - s(x)| < 2**-37.5 (~[-4.89e-12, 4.824e-12]). |
| 171 | const S1 = -0x15555554cbac77.0p-55; // -0.166666666416265235595 |
| 172 | const S2 = 0x111110896efbb2.0p-59; // 0.0083333293858894631756 |
| 173 | const S3 = -0x1a00f9e2cae774.0p-65; // -0.000198393348360966317347 |
| 174 | const S4 = 0x16cd878c3b46a7.0p-71; // 0.0000027183114939898219064 |
| 175 | |
| 176 | // Try to optimize for parallel evaluation as in __tandf.c. |
| 177 | const z = x * x; |
| 178 | const w = z * z; |
| 179 | const r = S3 + z * S4; |
| 180 | const s = z * x; |
| 181 | return @floatCast((x + s * (S1 + z * S2)) + s * w * r); |
| 182 | } |
| 183 | |
| 184 | pub fn sinx(x: f80, y: f80, iy: i32) f80 { |
| 185 | const S1: f80 = -0.166666666666666666671; |
| 186 | const S2: f64 = 0.0083333333333333332; |
| 187 | const S3: f64 = -0.00019841269841269427; |
| 188 | const S4: f64 = 0.0000027557319223597490; |
| 189 | const S5: f64 = -0.000000025052108218074604; |
| 190 | const S6: f64 = 1.6059006598854211e-10; |
| 191 | const S7: f64 = -7.6429779983024564e-13; |
| 192 | const S8: f64 = 2.6174587166648325e-15; |
| 193 | |
| 194 | const z = x * x; |
| 195 | const v = z * x; |
| 196 | const r = S2 + z * (S3 + z * (S4 + z * (S5 + |
| 197 | z * (S6 + z * (S7 + z * S8))))); |
| 198 | |
| 199 | if (iy == 0) |
| 200 | return x + v * (S1 + z * r); |
| 201 | |
| 202 | return x - ((z * (0.5 * y - v * r) - y) - v * S1); |
| 203 | } |
| 204 | |
| 205 | pub fn sinq(x: f128, y: f128, iy: i32) f128 { |
| 206 | const S1: f128 = -0.16666666666666666666666666666666666606732416116558; |
| 207 | const S2: f128 = 0.0083333333333333333333333333333331135404851288270047; |
| 208 | const S3: f128 = -0.00019841269841269841269841269839935785325638310428717; |
| 209 | const S4: f128 = 0.27557319223985890652557316053039946268333231205686e-5; |
| 210 | const S5: f128 = -0.25052108385441718775048214826384312253862930064745e-7; |
| 211 | const S6: f128 = 0.16059043836821614596571832194524392581082444805729e-9; |
| 212 | const S7: f128 = -0.76471637318198151807063387954939213287488216303768e-12; |
| 213 | const S8: f128 = 0.28114572543451292625024967174638477283187397621303e-14; |
| 214 | const S9: f64 = -0.82206352458348947812512122163446202498005154296863e-17; |
| 215 | const S10: f64 = 0.19572940011906109418080609928334380560135358385256e-19; |
| 216 | const S11: f64 = -0.38680813379701966970673724299207480965452616911420e-22; |
| 217 | const S12: f64 = 0.64038150078671872796678569586315881020659912139412e-25; |
| 218 | |
| 219 | const z = x * x; |
| 220 | const v = z * x; |
| 221 | const r = S2 + z * (S3 + z * (S4 + z * (S5 + z * (S6 + z * (S7 + z * (S8 + |
| 222 | z * (S9 + z * (S10 + z * (S11 + z * S12))))))))); |
| 223 | |
| 224 | if (iy == 0) |
| 225 | return x + v * (S1 + z * r); |
| 226 | |
| 227 | return x - ((z * (0.5 * y - v * r) - y) - v * S1); |
| 228 | } |
| 229 | |
| 230 | /// kernel tan function on ~[-pi/4, pi/4] (except on -0), pi/4 ~ 0.7854 |
| 231 | /// Input x is assumed to be bounded by ~pi/4 in magnitude. |
| 232 | /// Input y is the tail of x. |
| 233 | /// Input odd indicates whether tan (if odd = 0) or -1/tan (if odd = 1) is returned. |
| 234 | /// |
| 235 | /// Algorithm |
| 236 | /// 1. Since tan(-x) = -tan(x), we need only to consider positive x. |
| 237 | /// 2. Callers must return tan(-0) = -0 without calling here since our |
| 238 | /// odd polynomial is not evaluated in a way that preserves -0. |
| 239 | /// Callers may do the optimization tan(x) ~ x for tiny x. |
| 240 | /// 3. tan(x) is approximated by a odd polynomial of degree 27 on |
| 241 | /// [0,0.67434] |
| 242 | /// 3 27 |
| 243 | /// tan(x) ~ x + T1*x + ... + T13*x |
| 244 | /// where |
| 245 | /// |
| 246 | /// |tan(x) 2 4 26 | -59.2 |
| 247 | /// |----- - (1+T1*x +T2*x +.... +T13*x )| <= 2 |
| 248 | /// | x | |
| 249 | /// |
| 250 | /// Note: tan(x+y) = tan(x) + tan'(x)*y |
| 251 | /// ~ tan(x) + (1+x*x)*y |
| 252 | /// Therefore, for better accuracy in computing tan(x+y), let |
| 253 | /// 3 2 2 2 2 |
| 254 | /// r = x *(T2+x *(T3+x *(...+x *(T12+x *T13)))) |
| 255 | /// then |
| 256 | /// 3 2 |
| 257 | /// tan(x+y) = x + (T1*x + (x *(r+y)+y)) |
| 258 | /// |
| 259 | /// 4. For x in [0.67434,pi/4], let y = pi/4 - x, then |
| 260 | /// tan(x) = tan(pi/4-y) = (1-tan(y))/(1+tan(y)) |
| 261 | /// = 1 - 2*(tan(y) - (tan(y)^2)/(1+tan(y))) |
| 262 | pub fn tan(x_: f64, y_: f64, odd: bool) f64 { |
| 263 | var x = x_; |
| 264 | var y = y_; |
| 265 | |
| 266 | const T = [_]f64{ |
| 267 | 3.33333333333334091986e-01, // 3FD55555, 55555563 |
| 268 | 1.33333333333201242699e-01, // 3FC11111, 1110FE7A |
| 269 | 5.39682539762260521377e-02, // 3FABA1BA, 1BB341FE |
| 270 | 2.18694882948595424599e-02, // 3F9664F4, 8406D637 |
| 271 | 8.86323982359930005737e-03, // 3F8226E3, E96E8493 |
| 272 | 3.59207910759131235356e-03, // 3F6D6D22, C9560328 |
| 273 | 1.45620945432529025516e-03, // 3F57DBC8, FEE08315 |
| 274 | 5.88041240820264096874e-04, // 3F4344D8, F2F26501 |
| 275 | 2.46463134818469906812e-04, // 3F3026F7, 1A8D1068 |
| 276 | 7.81794442939557092300e-05, // 3F147E88, A03792A6 |
| 277 | 7.14072491382608190305e-05, // 3F12B80F, 32F0A7E9 |
| 278 | -1.85586374855275456654e-05, // BEF375CB, DB605373 |
| 279 | 2.59073051863633712884e-05, // 3EFB2A70, 74BF7AD4 |
| 280 | }; |
| 281 | const pio4 = 7.85398163397448278999e-01; // 3FE921FB, 54442D18 |
| 282 | const pio4lo = 3.06161699786838301793e-17; // 3C81A626, 33145C07 |
| 283 | |
| 284 | var z: f64 = undefined; |
| 285 | var r: f64 = undefined; |
| 286 | var v: f64 = undefined; |
| 287 | var w: f64 = undefined; |
| 288 | var s: f64 = undefined; |
| 289 | var a: f64 = undefined; |
| 290 | var w0: f64 = undefined; |
| 291 | var a0: f64 = undefined; |
| 292 | var hx: u32 = undefined; |
| 293 | var sign: bool = undefined; |
| 294 | |
| 295 | hx = @intCast(@as(u64, @bitCast(x)) >> 32); |
| 296 | const big = (hx & 0x7fffffff) >= 0x3FE59428; // |x| >= 0.6744 |
| 297 | if (big) { |
| 298 | sign = hx >> 31 != 0; |
| 299 | if (sign) { |
| 300 | x = -x; |
| 301 | y = -y; |
| 302 | } |
| 303 | x = (pio4 - x) + (pio4lo - y); |
| 304 | y = 0.0; |
| 305 | } |
| 306 | z = x * x; |
| 307 | w = z * z; |
| 308 | |
| 309 | // Break x^5*(T[1]+x^2*T[2]+...) into |
| 310 | // x^5(T[1]+x^4*T[3]+...+x^20*T[11]) + |
| 311 | // x^5(x^2*(T[2]+x^4*T[4]+...+x^22*[T12])) |
| 312 | r = T[1] + w * (T[3] + w * (T[5] + w * (T[7] + w * (T[9] + w * T[11])))); |
| 313 | v = z * (T[2] + w * (T[4] + w * (T[6] + w * (T[8] + w * (T[10] + w * T[12]))))); |
| 314 | s = z * x; |
| 315 | r = y + z * (s * (r + v) + y) + s * T[0]; |
| 316 | w = x + r; |
| 317 | if (big) { |
| 318 | s = @floatFromInt(1 - 2 * @as(i3, @intFromBool(odd))); |
| 319 | v = s - 2.0 * (x + (r - w * w / (w + s))); |
| 320 | return if (sign) -v else v; |
| 321 | } |
| 322 | if (!odd) { |
| 323 | return w; |
| 324 | } |
| 325 | // -1.0/(x+r) has up to 2ulp error, so compute it accurately |
| 326 | w0 = w; |
| 327 | w0 = @bitCast(@as(u64, @bitCast(w0)) & 0xffffffff00000000); |
| 328 | v = r - (w0 - x); // w0+v = r+x |
| 329 | a = -1.0 / w; |
| 330 | a0 = a; |
| 331 | a0 = @bitCast(@as(u64, @bitCast(a0)) & 0xffffffff00000000); |
| 332 | return a0 + a * (1.0 + a0 * w0 + a0 * v); |
| 333 | } |
| 334 | |
| 335 | pub fn tandf(x: f64, odd: bool) f32 { |
| 336 | // |tan(x)/x - t(x)| < 2**-25.5 (~[-2e-08, 2e-08]). |
| 337 | const T = [_]f64{ |
| 338 | 0x15554d3418c99f.0p-54, // 0.333331395030791399758 |
| 339 | 0x1112fd38999f72.0p-55, // 0.133392002712976742718 |
| 340 | 0x1b54c91d865afe.0p-57, // 0.0533812378445670393523 |
| 341 | 0x191df3908c33ce.0p-58, // 0.0245283181166547278873 |
| 342 | 0x185dadfcecf44e.0p-61, // 0.00297435743359967304927 |
| 343 | 0x1362b9bf971bcd.0p-59, // 0.00946564784943673166728 |
| 344 | }; |
| 345 | |
| 346 | const z = x * x; |
| 347 | // Split up the polynomial into small independent terms to give |
| 348 | // opportunities for parallel evaluation. The chosen splitting is |
| 349 | // micro-optimized for Athlons (XP, X64). It costs 2 multiplications |
| 350 | // relative to Horner's method on sequential machines. |
| 351 | // |
| 352 | // We add the small terms from lowest degree up for efficiency on |
| 353 | // non-sequential machines (the lowest degree terms tend to be ready |
| 354 | // earlier). Apart from this, we don't care about order of |
| 355 | // operations, and don't need to to care since we have precision to |
| 356 | // spare. However, the chosen splitting is good for accuracy too, |
| 357 | // and would give results as accurate as Horner's method if the |
| 358 | // small terms were added from highest degree down. |
| 359 | const r = T[4] + z * T[5]; |
| 360 | const t = T[2] + z * T[3]; |
| 361 | const w = z * z; |
| 362 | const s = z * x; |
| 363 | const u = T[0] + z * T[1]; |
| 364 | const r0 = (x + s * u) + (s * w) * (t + w * r); |
| 365 | return @floatCast(if (odd) -1.0 / r0 else r0); |
| 366 | } |
| 367 | |
| 368 | pub fn tanx(x_: f80, y_: f80, odd: i32) f80 { |
| 369 | const pio4: f80 = 0.785398163397448309628; |
| 370 | const pio4lo: f80 = -1.25413940316708300586e-20; |
| 371 | |
| 372 | const T3: f80 = 0.333333333333333333180; |
| 373 | const T5: f80 = 0.133333333333333372290; |
| 374 | const T7: f80 = 0.0539682539682504975744; |
| 375 | const T9: f64 = 0.021869488536312216; |
| 376 | const T11: f64 = 0.0088632355256619590; |
| 377 | const T13: f64 = 0.0035921281113786528; |
| 378 | const T15: f64 = 0.0014558334756312418; |
| 379 | const T17: f64 = 0.00059003538700862256; |
| 380 | const T19: f64 = 0.00023907843576635544; |
| 381 | const T21: f64 = 0.000097154625656538905; |
| 382 | const T23: f64 = 0.000038440165747303162; |
| 383 | const T25: f64 = 0.000018082171885432524; |
| 384 | const T27: f64 = 0.0000024196006108814377; |
| 385 | const T29: f64 = 0.0000078293456938132840; |
| 386 | const T31: f64 = -0.0000032609076735050182; |
| 387 | const T33: f64 = 0.0000023261313142559411; |
| 388 | |
| 389 | var x = x_; |
| 390 | var y = y_; |
| 391 | const big = @abs(x) >= 0.67434; |
| 392 | var sign: i8 = 0; |
| 393 | |
| 394 | if (big) { |
| 395 | if (x < 0) { |
| 396 | sign = -1; |
| 397 | x = -x; |
| 398 | y = -y; |
| 399 | } |
| 400 | x = (pio4 - x) + (pio4lo - y); |
| 401 | y = 0.0; |
| 402 | } |
| 403 | |
| 404 | var z = x * x; |
| 405 | var w = z * z; |
| 406 | |
| 407 | var r = T5 + w * (T9 + w * (T13 + w * (T17 + w * (T21 + |
| 408 | w * (T25 + w * (T29 + w * T33)))))); |
| 409 | |
| 410 | var v = z * (T7 + w * (T11 + w * (T15 + w * (T19 + w * (T23 + |
| 411 | w * (T27 + w * T31)))))); |
| 412 | |
| 413 | var s = z * x; |
| 414 | r = y + z * (s * (r + v) + y) + T3 * s; |
| 415 | w = x + r; |
| 416 | |
| 417 | if (big) { |
| 418 | s = @as(f80, @floatFromInt(1 - 2 * odd)); |
| 419 | v = s - 2.0 * (x + (r - w * w / (w + s))); |
| 420 | return if (sign == -1) -v else v; |
| 421 | } |
| 422 | |
| 423 | if (odd == 0) { |
| 424 | return w; |
| 425 | } |
| 426 | |
| 427 | // if allow error up to 2 ulp, simply return |
| 428 | // -1.0 / (x+r) here |
| 429 | // |
| 430 | // compute -1.0 / (x+r) accurately |
| 431 | z = w + 0x1p32 - 0x1p32; |
| 432 | v = r - (z - x); |
| 433 | const a = -1.0 / w; |
| 434 | const t = a + 0x1p32 - 0x1p32; |
| 435 | s = 1.0 + t * z; |
| 436 | return t + a * (s + t * v); |
| 437 | } |
| 438 | |
| 439 | pub fn tanq(x_: f128, y_: f128, odd: i32) f128 { |
| 440 | const pio4: f128 = 0x1.921fb54442d18469898cc51701b8p-1; |
| 441 | const pio4lo: f128 = 0x1.cd129024e088a67cc74020bbea60p-116; |
| 442 | |
| 443 | const T3: f128 = 0x1.5555555555555555555555555553p-2; |
| 444 | const T5: f128 = 0x1.1111111111111111111111111eb5p-3; |
| 445 | const T7: f128 = 0x1.ba1ba1ba1ba1ba1ba1ba1b694cd6p-5; |
| 446 | const T9: f128 = 0x1.664f4882c10f9f32d6bbe09d8bcdp-6; |
| 447 | const T11: f128 = 0x1.226e355e6c23c8f5b4f5762322eep-7; |
| 448 | const T13: f128 = 0x1.d6d3d0e157ddfb5fed8e84e27b37p-9; |
| 449 | const T15: f128 = 0x1.7da36452b75e2b5fce9ee7c2c92ep-10; |
| 450 | const T17: f128 = 0x1.355824803674477dfcf726649efep-11; |
| 451 | const T19: f128 = 0x1.f57d7734d1656e0aceb716f614c2p-13; |
| 452 | const T21: f128 = 0x1.967e18afcb180ed942dfdc518d6cp-14; |
| 453 | const T23: f128 = 0x1.497d8eea21e95bc7e2aa79b9f2cdp-15; |
| 454 | const T25: f128 = 0x1.0b132d39f055c81be49eff7afd50p-16; |
| 455 | const T27: f128 = 0x1.b0f72d33eff7bfa2fbc1059d90b6p-18; |
| 456 | const T29: f128 = 0x1.5ef2daf21d1113df38d0fbc00267p-19; |
| 457 | const T31: f128 = 0x1.1c77d6eac0234988cdaa04c96626p-20; |
| 458 | const T33: f128 = 0x1.cd2a5a292b180e0bdd701057dfe3p-22; |
| 459 | const T35: f128 = 0x1.75c7357d0298c01a31d0a6f7d518p-23; |
| 460 | const T37: f128 = 0x1.2f3190f4718a9a520f98f50081fcp-24; |
| 461 | const T39: f64 = 0.000000028443389121318352; |
| 462 | const T41: f64 = 0.000000011981013102001973; |
| 463 | const T43: f64 = 0.0000000038303578044958070; |
| 464 | const T45: f64 = 0.0000000034664378216909893; |
| 465 | const T47: f64 = -0.0000000015090641701997785; |
| 466 | const T49: f64 = 0.0000000029449552300483952; |
| 467 | const T51: f64 = -0.0000000022006995706097711; |
| 468 | const T53: f64 = 0.0000000015468200913196612; |
| 469 | const T55: f64 = -0.00000000061311613386849674; |
| 470 | const T57: f64 = 1.4912469681508012e-10; |
| 471 | |
| 472 | var x = x_; |
| 473 | var y = y_; |
| 474 | |
| 475 | const big = @abs(x) >= 0.67434; |
| 476 | var sign: i8 = 0; |
| 477 | |
| 478 | if (big) { |
| 479 | if (x < 0) { |
| 480 | sign = -1; |
| 481 | x = -x; |
| 482 | y = -y; |
| 483 | } |
| 484 | x = (pio4 - x) + (pio4lo - y); |
| 485 | y = 0.0; |
| 486 | } |
| 487 | |
| 488 | var z = x * x; |
| 489 | var w = z * z; |
| 490 | |
| 491 | var r = T5 + w * (T9 + w * (T13 + w * (T17 + w * (T21 + |
| 492 | w * (T25 + w * (T29 + w * (T33 + w * (T37 + w * (T41 + |
| 493 | w * (T45 + w * (T49 + w * (T53 + w * T57)))))))))))); |
| 494 | |
| 495 | var v = z * (T7 + w * (T11 + w * (T15 + w * (T19 + w * (T23 + |
| 496 | w * (T27 + w * (T31 + w * (T35 + w * (T39 + w * (T43 + |
| 497 | w * (T47 + w * (T51 + w * T55)))))))))))); |
| 498 | |
| 499 | var s = z * x; |
| 500 | r = y + z * (s * (r + v) + y) + T3 * s; |
| 501 | w = x + r; |
| 502 | |
| 503 | if (big) { |
| 504 | s = @as(f128, @floatFromInt(1 - 2 * odd)); |
| 505 | v = s - 2.0 * (x + (r - w * w / (w + s))); |
| 506 | return if (sign == -1) -v else v; |
| 507 | } |
| 508 | |
| 509 | if (odd == 0) { |
| 510 | return w; |
| 511 | } |
| 512 | |
| 513 | // if allow error up to 2 ulp, simply return |
| 514 | // -1.0 / (x+r) here |
| 515 | // |
| 516 | // compute -1.0 / (x+r) accurately |
| 517 | z = w + 0x1p32 - 0x1p32; |
| 518 | v = r - (z - x); |
| 519 | const a = -1.0 / w; |
| 520 | const t = a + 0x1p32 - 0x1p32; |
| 521 | s = 1.0 + t * z; |
| 522 | return t + a * (s + t * v); |
| 523 | } |