time-to-botec

Benchmark sampling in different programming languages
Log | Files | Refs | README

erfc.js (10704B)


      1 /**
      2 * @license Apache-2.0
      3 *
      4 * Copyright (c) 2018 The Stdlib Authors.
      5 *
      6 * Licensed under the Apache License, Version 2.0 (the "License");
      7 * you may not use this file except in compliance with the License.
      8 * You may obtain a copy of the License at
      9 *
     10 *    http://www.apache.org/licenses/LICENSE-2.0
     11 *
     12 * Unless required by applicable law or agreed to in writing, software
     13 * distributed under the License is distributed on an "AS IS" BASIS,
     14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     15 * See the License for the specific language governing permissions and
     16 * limitations under the License.
     17 *
     18 *
     19 * ## Notice
     20 *
     21 * The following copyright, license, and long comment were part of the original implementation available as part of [FreeBSD]{@link https://svnweb.freebsd.org/base/release/9.3.0/lib/msun/src/s_erf.c}. The implementation follows the original, but has been modified for JavaScript.
     22 *
     23 * ```text
     24 * Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
     25 *
     26 * Developed at SunPro, a Sun Microsystems, Inc. business.
     27 * Permission to use, copy, modify, and distribute this
     28 * software is freely granted, provided that this notice
     29 * is preserved.
     30 * ```
     31 */
     32 
     33 'use strict';
     34 
     35 // MODULES //
     36 
     37 var isnan = require( './../../../../base/assert/is-nan' );
     38 var exp = require( './../../../../base/special/exp' );
     39 var setLowWord = require( '@stdlib/number/float64/base/set-low-word' );
     40 var PINF = require( '@stdlib/constants/float64/pinf' );
     41 var NINF = require( '@stdlib/constants/float64/ninf' );
     42 var polyvalPP = require( './polyval_pp.js' );
     43 var polyvalQQ = require( './polyval_qq.js' );
     44 var polyvalPA = require( './polyval_pa.js' );
     45 var polyvalQA = require( './polyval_qa.js' );
     46 var polyvalRA = require( './polyval_ra.js' );
     47 var polyvalSA = require( './polyval_sa.js' );
     48 var polyvalRB = require( './polyval_rb.js' );
     49 var polyvalSB = require( './polyval_sb.js' );
     50 
     51 
     52 // VARIABLES //
     53 
     54 var TINY = 1.0e-300;
     55 
     56 // 2**-56 = 1/(2**56) = 1/72057594037927940
     57 var SMALL = 1.3877787807814457e-17;
     58 
     59 var ERX = 8.45062911510467529297e-1;  // 0x3FEB0AC1, 0x60000000
     60 
     61 var PPC = 1.28379167095512558561e-1;  // 0x3FC06EBA, 0x8214DB68
     62 var QQC = 1.0;
     63 
     64 var PAC = -2.36211856075265944077e-3; // 0xBF6359B8, 0xBEF77538
     65 var QAC = 1.0;
     66 
     67 var RAC = -9.86494403484714822705e-3; // 0xBF843412, 0x600D6435
     68 var SAC = 1.0;
     69 
     70 var RBC = -9.86494292470009928597e-3; // 0xBF843412, 0x39E86F4A
     71 var SBC = 1.0;
     72 
     73 
     74 // MAIN //
     75 
     76 /**
     77 * Evaluates the complementary error function.
     78 *
     79 * ```tex
     80 * \operatorname{erf}(x) = \frac{2}{\sqrt{\pi}} \int^{x}_{0} e^{-t^2}\ \mathrm{dt}
     81 * ```
     82 *
     83 * Note that
     84 *
     85 * ```tex
     86 * \begin{align*}
     87 * \operatorname{erfc}(x) &= 1 - \operatorname{erf}(x) \\
     88 * \operatorname{erf}(-x) &= -\operatorname{erf}(x) \\
     89 * \operatorname{erfc}(-x) &= 2 - \operatorname{erfc}(x)
     90 * \end{align*}
     91 * ```
     92 *
     93 * ## Method
     94 *
     95 * 1.  For \\(|x| \in [0, 0.84375)\\),
     96 *
     97 *     ```tex
     98 *     \operatorname{erf}(x) = x + x \cdot \operatorname{R}(x^2)
     99 *     ```
    100 *
    101 *     and
    102 *
    103 *     ```tex
    104 *     \operatorname{erfc}(x) = \begin{cases}
    105 *     1 - \operatorname{erf}(x) & \textrm{if}\ x \in (-.84375,0.25) \\
    106 *     0.5 + ((0.5-x)-x \mathrm{R}) & \textrm{if}\ x \in [0.25,0.84375)
    107 *     \end{cases}
    108 *     ```
    109 *
    110 *     where \\(R = P/Q\\) and where \\(P\\) is an odd polynomial of degree \\(8\\) and \\(Q\\) is an odd polynomial of degree \\(10\\).
    111 *
    112 *     ```tex
    113 *     \biggl| \mathrm{R} - \frac{\operatorname{erf}(x)-x}{x} \biggr| \leq 2^{-57.90}
    114 *     ```
    115 *
    116 *     <!-- <note> -->
    117 *
    118 *     The formula is derived by noting
    119 *
    120 *     ```tex
    121 *     \operatorname{erf}(x) = \frac{2}{\sqrt{\pi}}\biggl(x - \frac{x^3}{3} + \frac{x^5}{10} - \frac{x^7}{42} + \ldots \biggr)
    122 *     ```
    123 *
    124 *     and that
    125 *
    126 *     ```tex
    127 *     \frac{2}{\sqrt{\pi}} = 1.128379167095512573896158903121545171688
    128 *     ```
    129 *
    130 *     is close to unity. The interval is chosen because the fix point of \\(\operatorname{erf}(x)\\) is near \\(0.6174\\) (i.e., \\(\operatorname{erf(x)} = x\\) when \\(x\\) is near \\(0.6174\\)), and, by some experiment, \\(0.84375\\) is chosen to guarantee the error is less than one ulp for \\(\operatorname{erf}(x)\\).
    131 *
    132 *     <!-- </note> -->
    133 *
    134 * 2.  For \\(|x| \in [0.84375,1.25)\\), let \\(s = |x|-1\\), and \\(c = 0.84506291151\\) rounded to single (\\(24\\) bits)
    135 *
    136 *     ```tex
    137 *     \operatorname{erf}(x) = \operatorname{sign}(x) \cdot \biggl(c + \frac{\operatorname{P1}(s)}{\operatorname{Q1}(s)}\biggr)
    138 *     ```
    139 *
    140 *     and
    141 *
    142 *     ```tex
    143 *     \operatorname{erfc}(x) = \begin{cases}
    144 *     (1-c) - \frac{\operatorname{P1}(s)}{\operatorname{Q1}(s)} & \textrm{if}\ x > 0 \\
    145 *     1 + \biggl(c + \frac{\operatorname{P1}(s)}{\operatorname{Q1}(s)}\biggr) & \textrm{if}\ x < 0
    146 *     \end{cases}
    147 *     ```
    148 *
    149 *     where
    150 *
    151 *     ```tex
    152 *     \biggl|\frac{\mathrm{P1}}{\mathrm{Q1}} - (\operatorname{erf}(|x|)-c)\biggr| \leq 2^{-59.06}
    153 *     ```
    154 *
    155 *     <!-- <note> -->
    156 *
    157 *     Here, we use the Taylor series expansion at \\(x = 1\\)
    158 *
    159 *     ```tex
    160 *     \begin{align*}
    161 *     \operatorname{erf}(1+s) &= \operatorname{erf}(1) + s\cdot \operatorname{poly}(s) \\
    162 *     &= 0.845.. + \frac{\operatorname{P1}(s)}{\operatorname{Q1}(s)}
    163 *     \end{align*}
    164 *     ```
    165 *
    166 *     using a rational approximation to approximate
    167 *
    168 *     ```tex
    169 *     \operatorname{erf}(1+s) - (c = (\mathrm{single})0.84506291151)
    170 *     ```
    171 *
    172 *     <!-- </note> -->
    173 *
    174 *     Note that, for \\(x \in [0.84375,1.25)\\), \\(|\mathrm{P1}/\mathrm{Q1}| < 0.078\\), where
    175 *
    176 *     -   \\(\operatorname{P1}(s)\\) is a degree \\(6\\) polynomial in \\(s\\)
    177 *     -   \\(\operatorname{Q1}(s)\\) is a degree \\(6\\) polynomial in \\(s\\)
    178 *
    179 * 3.  For \\(x \in [1.25,1/0.35)\\),
    180 *
    181 *     ```tex
    182 *     \begin{align*}
    183 *     \operatorname{erfc}(x) &= \frac{1}{x}e^{-x^2-0.5625+(\mathrm{R1}/\mathrm{S1})} \\
    184 *     \operatorname{erf}(x) &= 1 - \operatorname{erfc}(x)
    185 *     \end{align*}
    186 *     ```
    187 *
    188 *     where
    189 *
    190 *     -   \\(\operatorname{R1}(z)\\) is a degree \\(7\\) polynomial in \\(z\\), where \\(z = 1/x^2\\)
    191 *     -   \\(\operatorname{S1}(z)\\) is a degree \\(8\\) polynomial in \\(z\\)
    192 *
    193 * 4.  For \\(x \in [1/0.35,28)\\),
    194 *
    195 *     ```tex
    196 *     \operatorname{erfc}(x) = \begin{cases}
    197 *     \frac{1}{x} e^{-x^2-0.5625+(\mathrm{R2}/\mathrm{S2})} & \textrm{if}\ x > 0 \\
    198 *     2.0 - \frac{1}{x} e^{-x^2-0.5625+(\mathrm{R2}/\mathrm{S2})} & \textrm{if}\ -6 < x < 0 \\
    199 *     2.0 - \mathrm{tiny} & \textrm{if}\ x \leq -6
    200 *     \end{cases}
    201 *     ```
    202 *
    203 *     and
    204 *
    205 *     ```tex
    206 *     \operatorname{erf}(x) = \begin{cases}
    207 *     \operatorname{sign}(x) \cdot (1.0 - \operatorname{erfc}(x)) & \textrm{if}\ x < 6 \\
    208 *     \operatorname{sign}(x) \cdot (1.0 - \mathrm{tiny}) & \textrm{otherwise}
    209 *     \end{cases}
    210 *     ```
    211 *
    212 *     where
    213 *
    214 *     -   \\(\operatorname{R2}(z)\\) is a degree \\(6\\) polynomial in \\(z\\), where \\(z = 1/x^2\\)
    215 *     -   \\(\operatorname{S2}(z)\\) is a degree \\(7\\) polynomial in \\(z\\)
    216 *
    217 * 5.  For \\(x \in [28, \infty)\\),
    218 *
    219 *     ```tex
    220 *     \begin{align*}
    221 *     \operatorname{erf}(x) &= \operatorname{sign}(x) \cdot (1 - \mathrm{tiny}) & \textrm{(raise inexact)}
    222 *     \end{align*}
    223 *     ```
    224 *
    225 *     and
    226 *
    227 *     ```tex
    228 *     \operatorname{erfc}(x) = \begin{cases}
    229 *     \mathrm{tiny} \cdot \mathrm{tiny} & \textrm{if}\ x > 0\ \textrm{(raise underflow)} \\
    230 *     2 - \mathrm{tiny} & \textrm{if}\ x < 0
    231 *     \end{cases}
    232 *     ```
    233 *
    234 *
    235 * ## Special Cases
    236 *
    237 * ```tex
    238 * \begin{align*}
    239 * \operatorname{erf}(0) &= 0 \\
    240 * \operatorname{erf}(-0) &= -0 \\
    241 * \operatorname{erf}(\infty) &= 1 \\
    242 * \operatorname{erf}(-\infty) &= -1 \\
    243 * \operatorname{erfc}(0) &= 1 \\
    244 * \operatorname{erfc}(\infty) &= 0 \\
    245 * \operatorname{erfc}(-\infty) &= 2 \\
    246 * \operatorname{erf}(\mathrm{NaN}) &= \mathrm{NaN} \\
    247 * \operatorname{erfc}(\mathrm{NaN}) &= \mathrm{NaN}
    248 * \end{align*}
    249 * ```
    250 *
    251 *
    252 * ## Notes
    253 *
    254 * -   To compute \\(\exp(-x^2-0.5625+(\mathrm{R}/\mathrm{S}))\\), let \\(s\\) be a single precision number and \\(s := x\\); then
    255 *
    256 *     ```tex
    257 *     -x^2 = -s^2 + (s-x)(s+x)
    258 *     ```
    259 *
    260 *     and
    261 *
    262 *     ```tex
    263 *     e^{-x^2-0.5626+(\mathrm{R}/\mathrm{S})} = e^{-s^2-0.5625} e^{(s-x)(s+x)+(\mathrm{R}/\mathrm{S})}
    264 *     ```
    265 *
    266 * -   `#4` and `#5` make use of the asymptotic series
    267 *
    268 *     ```tex
    269 *     \operatorname{erfc}(x) \approx \frac{e^{-x^2}}{x\sqrt{\pi}} (1 + \operatorname{poly}(1/x^2))
    270 *     ```
    271 *
    272 *     We use a rational approximation to approximate
    273 *
    274 *     ```tex
    275 *     g(s) = f(1/x^2) = \ln(\operatorname{erfc}(x) \cdot x) - x^2 + 0.5625
    276 *     ```
    277 *
    278 * -   The error bound for \\(\mathrm{R1}/\mathrm{S1}\\) is
    279 *
    280 *     ```tex
    281 *     |\mathrm{R1}/\mathrm{S1} - f(x)| < 2^{-62.57}
    282 *     ```
    283 *
    284 *     and for \\(\mathrm{R2}/\mathrm{S2}\\) is
    285 *
    286 *     ```tex
    287 *     |\mathrm{R2}/\mathrm{S2} - f(x)| < 2^{-61.52}
    288 *     ```
    289 *
    290 * @param {number} x - input value
    291 * @returns {number} function value
    292 *
    293 * @example
    294 * var y = erfc( 2.0 );
    295 * // returns ~0.0047
    296 *
    297 * @example
    298 * var y = erfc( -1.0 );
    299 * // returns ~1.8427
    300 *
    301 * @example
    302 * var y = erfc( 0.0 );
    303 * // returns 1.0
    304 *
    305 * @example
    306 * var y = erfc( Infinity );
    307 * // returns 0.0
    308 *
    309 * @example
    310 * var y = erfc( -Infinity );
    311 * // returns 2.0
    312 *
    313 * @example
    314 * var y = erfc( NaN );
    315 * // returns NaN
    316 */
    317 function erfc( x ) {
    318 	var sign;
    319 	var ax;
    320 	var z;
    321 	var r;
    322 	var s;
    323 	var y;
    324 	var p;
    325 	var q;
    326 
    327 	// Special case: NaN
    328 	if ( isnan( x ) ) {
    329 		return NaN;
    330 	}
    331 	// Special case: +infinity
    332 	if ( x === PINF ) {
    333 		return 0.0;
    334 	}
    335 	// Special case: -infinity
    336 	if ( x === NINF ) {
    337 		return 2.0;
    338 	}
    339 	// Special case: +-0
    340 	if ( x === 0.0 ) {
    341 		return 1.0;
    342 	}
    343 	if ( x < 0.0 ) {
    344 		sign = true;
    345 		ax = -x;
    346 	} else {
    347 		sign = false;
    348 		ax = x;
    349 	}
    350 	// |x| < 0.84375
    351 	if ( ax < 0.84375 ) {
    352 		if ( ax < SMALL ) {
    353 			return 1.0 - x; // raise inexact
    354 		}
    355 		z = x * x;
    356 		r = PPC + ( z*polyvalPP( z ) );
    357 		s = QQC + ( z*polyvalQQ( z ) );
    358 		y = r / s;
    359 
    360 		// x < 1/4
    361 		if ( x < 0.25 ) {
    362 			return 1.0 - ( x + (x*y) );
    363 		}
    364 		r = x * y;
    365 		r += x - 0.5;
    366 		return 0.5 - r;
    367 	}
    368 	// 0.84375 <= |x| < 1.25
    369 	if ( ax < 1.25 ) {
    370 		s = ax - 1.0;
    371 		p = PAC + ( s*polyvalPA( s ) );
    372 		q = QAC + ( s*polyvalQA( s ) );
    373 		if ( sign ) {
    374 			return 1.0 + ERX + (p/q);
    375 		}
    376 		return 1.0 - ERX - (p/q);
    377 	}
    378 	// |x| < 28
    379 	if ( ax < 28.0 ) {
    380 		s = 1.0 / (ax*ax);
    381 
    382 		// |x| < 1/0.35 ~ 2.857143
    383 		if ( ax < 2.857142857142857 ) {
    384 			r = RAC + ( s*polyvalRA( s ) );
    385 			s = SAC + ( s*polyvalSA( s ) );
    386 		}
    387 		// |x| >= 1/0.35 ~ 2.857143
    388 		else {
    389 			// x < -6
    390 			if ( x < -6.0 ) {
    391 				return 2.0 - TINY; // raise inexact
    392 			}
    393 			r = RBC + ( s*polyvalRB( s ) );
    394 			s = SBC + ( s*polyvalSB( s ) );
    395 		}
    396 		z = setLowWord( ax, 0 ); // pseudo-single (20-bit) precision x
    397 		r = exp( -(z*z) - 0.5625 ) * exp( ((z-ax)*(z+ax)) + (r/s) );
    398 		if ( sign ) {
    399 			return 2.0 - (r/ax);
    400 		}
    401 		return r/ax;
    402 	}
    403 	if ( sign ) {
    404 		return 2.0 - TINY; // raise inexact; ~2
    405 	}
    406 	return TINY * TINY; // raise inexact; ~0
    407 }
    408 
    409 
    410 // EXPORTS //
    411 
    412 module.exports = erfc;