this repo has no description
at fixPythonPipStalling 316 lines 9.4 kB view raw
1/* This is an implementation of asin. It is written in standard C except: 2 3 double is expected be an IEEE 754 double-precision implementation. 4 5 "volatile" is used to attempt to force certain floating-point 6 operations to occur at run time (to generate exceptions that might not 7 be generated if the operations are performed at compile time). 8 9 long double is used for extended precision if it has at least 64 bits 10 of precision. Otherwise, double is used with some cumbersome routines 11 to provide multiple precision arithmetic, resulting in precise and 12 accurate but slow code. Other techniques, such as dividing the work 13 into smaller intervals, were tried briefly but did not yield 14 satisfactory solutions in the time allowed. 15*/ 16 17 18// Include math.h to ensure we match the declarations. 19#include <math.h> 20 21// Include float.h so we can check the characteristics of the long double type. 22#include <float.h> 23#if 64 <= LDBL_MANT_DIG 24 #define UseLongDouble 1 25#endif 26 27 28/* Declare certain constants volatile to force the compiler to access them 29 when we reference them. This in turn forces arithmetic operations on them 30 to be performed at run time (or as if at run time). We use such operations 31 to generate exceptions such as invalid or inexact. 32*/ 33static volatile const double Infinity = INFINITY; 34static volatile const double Tiny = 0x1p-1022; 35 36 37#if defined __STDC__ && 199901L <= __STDC_VERSION__ && !defined __GNUC__ 38 // GCC does not currently support FENV_ACCESS. Maybe someday. 39 #pragma STDC FENV_ACCESS ON 40#endif 41 42 43#if !UseLongDouble 44 45 46 /* If we are not using long double arithmetic, define some subroutines 47 to provided extended precision using double arithmetic. 48 49 These routines are derived from CR-LIBM: A Library of correctly 50 rounded elementary functions in double-precision by Catherine 51 Daramy-Loirat, David Defour, Florent de Dinechin, Matthieu Gallet, 52 Nicolas Gast, Jean-Michel Muller. 53 54 The digits in the names indicate the precisions of the arguments and 55 results. For example, Add112 adds two numbers expressed with one 56 double each and returns a number expressed with two doubles. 57 */ 58 59 60 // double2 represents a number equal to d0 + d1, with |d1| <= 1/2 ULP(d0). 61 typedef struct { double d0, d1; } double2; 62 63 64 // Return a * b, given |a|, |b| < 2**970. 65 static inline double2 Mul112(double a, double b) 66 { 67 static const double c = 0x1p27 + 1; 68 69 double 70 ap = a * c, bp = b * c, 71 a0 = (a-ap)+ap, b0 = (b-bp)+bp, 72 a1 = a - a0, b1 = b - b0, 73 d0 = a * b, 74 d1 = a0*b0 - d0 + a0*b1 + a1*b0 + a1*b1; 75 return (double2) { d0, d1 }; 76 } 77 78 79 // Return a + b with relative error below 2**-103 given |b| < |a|. 80 static inline double2 Add212RightSmaller(double2 a, double b) 81 { 82 double 83 c0 = a.d0 + b, 84 c1 = a.d0 - c0 + b + a.d1, 85 d0 = c0 + c1, 86 d1 = c0 - d0 + c1; 87 return (double2) { d0, d1 }; 88 } 89 90 91 /* Return a + b with relative error below 2**-103 and then rounded to 92 double given |b| < |a|. 93 */ 94 static inline double Add221RightSmaller(double2 a, double2 b) 95 { 96 double 97 c0 = a.d0 + b.d0, 98 c1 = a.d0 - c0 + b.d0 + b.d1 + a.d1, 99 d0 = c0 + c1; 100 return d0; 101 } 102 103 104 /* Return approximately a * b - 1 given |a|, |b| < 2**970 and a * b is 105 very near 1. 106 */ 107 static inline double Mul121Special(double a, double2 b) 108 { 109 static const double c = 0x1p27 + 1; 110 111 double 112 ap = a * c, bp = b.d0 * c, 113 a0 = (a-ap)+ap, b0 = (b.d0-bp)+bp, 114 a1 = a - a0, b1 = b.d0 - b0, 115 m1 = a0*b0 - 1 + a0*b1 + a1*b0 + a1*b1 + a*b.d1; 116 return m1; 117 } 118 119 120 // Return a * b with relative error below 2**-102 given |a|, |b| < 2**970. 121 static inline double2 Mul222(double2 a, double2 b) 122 { 123 static const double c = 0x1p27 + 1; 124 125 double 126 ap = a.d0 * c, bp = b.d0 * c, 127 a0 = (a.d0-ap)+ap, b0 = (b.d0-bp)+bp, 128 a1 = a.d0 - a0, b1 = b.d0 - b0, 129 m0 = a.d0 * b.d0, 130 m1 = a0*b0 - m0 + a0*b1 + a1*b0 + a1*b1 + (a.d0*b.d1 + a.d1*b.d0), 131 d0 = m0 + m1, 132 d1 = m0 - d0 + m1; 133 return (double2) { d0, d1 }; 134 } 135 136 137#endif // !UseLongDouble 138 139 140/* double asin(double x). 141 142 (This routine appears below, following the Tail subroutine.) 143 144 Notes: 145 146 Citations in parentheses below indicate the source of a requirement. 147 148 "C" stands for ISO/IEC 9899:TC2. 149 150 The Open Group specification (IEEE Std 1003.1, 2004 edition) adds no 151 requirements since it defers to C and requires errno behavior only if 152 we choose to support it by arranging for "math_errhandling & 153 MATH_ERRNO" to be non-zero, which we do not. 154 155 Return value: 156 157 For arcsine of +/- zero, return zero with same sign (C F.9 12 and 158 F.9.1.2). 159 160 For 1 < |x| (including infinity), return NaN (C F.9.1.2). 161 162 For a NaN, return the same NaN (C F.9 11 and 13). (If the NaN is a 163 signalling NaN, we return the "same" NaN quieted.) 164 165 Otherwise: 166 167 If the rounding mode is round-to-nearest, return arcsine(x) 168 faithfully rounded. This is not proven but seems likely. 169 Generally, the largest source of errors is the evaluation of the 170 polynomial using double precision. Some analysis might bound this 171 and prove faithful rounding. The largest observed error is .678 172 ULP. 173 174 Return a value in [-pi/2, +pi/2] (C 7.12.4.2 3). 175 176 Not implemented: In other rounding modes, return arcsine(x) 177 possibly with slightly worse error, not necessarily honoring 178 the rounding mode (Ali Sazegari narrowing C F.9 10). 179 180 Exceptions: 181 182 Raise underflow for a denormal result (C F.9 7 and Draft Standard for 183 Floating-Point Arithmetic P754 Draft 1.2.5 9.5). If the input is the 184 smallest normal, underflow may or may not be raised. This is stricter 185 than the older 754 standard. 186 187 May or may not raise inexact, even if the result is exact (C F.9 8). 188 189 Raise invalid if the input is a signalling NaN (C 5.2.4.2.2 3, in spite 190 of C 4.2.1) or 1 < |x| (including infinity) (C F.9.1.2) but not if the 191 input is a quiet NaN (C F.9 11). 192 193 May not raise exceptions otherwise (C F.9 9). 194 195 Properties: 196 197 Not proven: Monotonic. 198*/ 199 200 201// Return arcsine(x) given |x| <= .5, with the same properties as asin. 202static double Center(double x) 203{ 204 if (-0x1.7137449123ef5p-26 <= x && x <= +0x1.7137449123ef5p-26) 205 return -0x1p-1022 < x && x < +0x1p-1022 206 // Generate underflow and inexact and return x. 207 ? x - x*x 208 // Generate inexact and return x. 209 : x * (Tiny + 1); 210 211 static const double p03 = 0.1666666666666558995379880; 212 static const double p05 = 0.0750000000029696112392353; 213 static const double p07 = 0.0446428568582815922683933; 214 static const double p09 = 0.0303819580081956423799529; 215 static const double p11 = 0.0223717830666671020710108; 216 static const double p13 = 0.0173593516996479249428647; 217 static const double p15 = 0.0138885410156894774969889; 218 static const double p17 = 0.0121483892822292648695383; 219 static const double p19 = 0.0066153165197009078340075; 220 static const double p21 = 0.0192942786775238654913582; 221 static const double p23 = -0.0158620440988475212803145; 222 static const double p25 = 0.0316658385792867081040808; 223 224 // Square x. 225 double x2 = x * x; 226 227 return (((((((((((( 228 + p25) * x2 229 + p23) * x2 230 + p21) * x2 231 + p19) * x2 232 + p17) * x2 233 + p15) * x2 234 + p13) * x2 235 + p11) * x2 236 + p09) * x2 237 + p07) * x2 238 + p05) * x2 239 + p03) * x2 * x + x; 240} 241 242 243// Return arcsine(x) given .5 < x, with the same properties as asin. 244static double Tail(double x) 245{ 246 if (1 <= x) 247 return 1 == x 248 // If x is 1, generate inexact and return Pi/2 rounded down. 249 ? 0x3.243f6a8885a308d313198a2e03707344ap-1 + Tiny 250 // If x is outside the domain, generate invalid and return NaN. 251 : Infinity - Infinity; 252 253 static const double p01 = 0.2145993335526539017488949; 254 static const double p02 = -0.0890259194305537131666744; 255 static const double p03 = 0.0506659694457588602631748; 256 static const double p04 = -0.0331919619444009606270380; 257 static const double p05 = 0.0229883479552557203133368; 258 static const double p06 = -0.0156746038587246716524035; 259 static const double p07 = 0.0097868293573384001221447; 260 static const double p08 = -0.0052049731575223952626203; 261 static const double p09 = 0.0021912255981979442677477; 262 static const double p10 = -0.0006702485124770180942917; 263 static const double p11 = 0.0001307564187657962919394; 264 static const double p12 = -0.0000121189820098929624806; 265 266 double polynomial = (((((((((((( 267 + p12) * x 268 + p11) * x 269 + p10) * x 270 + p09) * x 271 + p08) * x 272 + p07) * x 273 + p06) * x 274 + p05) * x 275 + p04) * x 276 + p03) * x 277 + p02) * x 278 + p01) * x; 279 280 #if UseLongDouble 281 static const long double HalfPi = 0x3.243f6a8885a308d313198a2ep-1L; 282 static const long double p00 = -1.5707961988153774692344105L; 283 284 return HalfPi + sqrtl(1-x) * (polynomial + p00); 285 #else // #if UseLongDouble 286 static const double2 287 HalfPi = { 0x1.921fb54442d18p+0, 0x1.1a62633145c07p-54 }, 288 p00 = { -0x1.921fb31e97d96p0, +0x1.eab77149ad27cp-54 }; 289 290 // Estimate 1 / sqrt(1-x). 291 double e = 1 / sqrt(1-x); 292 293 double2 ex = Mul112(e, 1-x); // e * x. 294 double e2x = Mul121Special(e, ex); // e**2 * x - 1. 295 296 // Set t0 to an improved approximation of sqrt(1-x) with Newton-Raphson. 297 double2 t0 = Add212RightSmaller(ex, ex.d0 * -.5 * e2x); 298 299 // Calculate pi/2 + sqrt(1-x) * p(x). 300 return Add221RightSmaller(HalfPi, Mul222( 301 t0, 302 Add212RightSmaller(p00, polynomial))); 303 #endif // #if UseLongDouble 304} 305 306 307// See documentation above. 308double asin(double x) 309{ 310 if (x < -.5) 311 return -Tail(-x); 312 else if (x <= .5) 313 return Center(x); 314 else 315 return +Tail(+x); 316}