time-to-botec

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

erf.js (10649B)


      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 var VERY_TINY = 2.848094538889218e-306; // 0x00800000, 0x00000000
     56 
     57 // 2**-28 = 1/(1<<28) = 1/268435456
     58 var SMALL = 3.725290298461914e-9;
     59 
     60 var ERX = 8.45062911510467529297e-1;  // 0x3FEB0AC1, 0x60000000
     61 
     62 var EFX = 1.28379167095512586316e-1;  // 0x3FC06EBA, 0x8214DB69
     63 var EFX8 = 1.02703333676410069053;    // 0x3FF06EBA, 0x8214DB69
     64 
     65 var PPC = 1.28379167095512558561e-1;  // 0x3FC06EBA, 0x8214DB68
     66 var QQC = 1.0;
     67 
     68 var PAC = -2.36211856075265944077e-3; // 0xBF6359B8, 0xBEF77538
     69 var QAC = 1.0;
     70 
     71 var RAC = -9.86494403484714822705e-3; // 0xBF843412, 0x600D6435
     72 var SAC = 1.0;
     73 
     74 var RBC = -9.86494292470009928597e-3; // 0xBF843412, 0x39E86F4A
     75 var SBC = 1.0;
     76 
     77 
     78 // MAIN //
     79 
     80 /**
     81 * Evaluates the error function.
     82 *
     83 * ```tex
     84 * \operatorname{erf}(x) = \frac{2}{\sqrt{\pi}} \int^{x}_{0} e^{-t^2}\ \mathrm{dt}
     85 * ```
     86 *
     87 * Note that
     88 *
     89 * ```tex
     90 * \begin{align*}
     91 * \operatorname{erfc}(x) &= 1 - \operatorname{erf}(x) \\
     92 * \operatorname{erf}(-x) &= -\operatorname{erf}(x) \\
     93 * \operatorname{erfc}(-x) &= 2 - \operatorname{erfc}(x)
     94 * \end{align*}
     95 * ```
     96 *
     97 * ## Method
     98 *
     99 * 1.  For \\(|x| \in [0, 0.84375)\\),
    100 *
    101 *     ```tex
    102 *     \operatorname{erf}(x) = x + x \cdot \operatorname{R}(x^2)
    103 *     ```
    104 *
    105 *     and
    106 *
    107 *     ```tex
    108 *     \operatorname{erfc}(x) = \begin{cases}
    109 *     1 - \operatorname{erf}(x) & \textrm{if}\ x \in (-.84375,0.25) \\
    110 *     0.5 + ((0.5-x)-x \mathrm{R}) & \textrm{if}\ x \in [0.25,0.84375)
    111 *     \end{cases}
    112 *     ```
    113 *
    114 *     where \\(R = P/Q\\) and where \\(P\\) is an odd polynomial of degree \\(8\\) and \\(Q\\) is an odd polynomial of degree \\(10\\).
    115 *
    116 *     ```tex
    117 *     \biggl| \mathrm{R} - \frac{\operatorname{erf}(x)-x}{x} \biggr| \leq 2^{-57.90}
    118 *     ```
    119 *
    120 *     <!-- <note> -->
    121 *
    122 *     The formula is derived by noting
    123 *
    124 *     ```tex
    125 *     \operatorname{erf}(x) = \frac{2}{\sqrt{\pi}}\biggl(x - \frac{x^3}{3} + \frac{x^5}{10} - \frac{x^7}{42} + \ldots \biggr)
    126 *     ```
    127 *
    128 *     and that
    129 *
    130 *     ```tex
    131 *     \frac{2}{\sqrt{\pi}} = 1.128379167095512573896158903121545171688
    132 *     ```
    133 *
    134 *     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)\\).
    135 *
    136 *     <!-- </note> -->
    137 *
    138 * 2.  For \\(|x| \in [0.84375,1.25)\\), let \\(s = |x|-1\\), and \\(c = 0.84506291151\\) rounded to single (\\(24\\) bits)
    139 *
    140 *     ```tex
    141 *     \operatorname{erf}(x) = \operatorname{sign}(x) \cdot \biggl(c + \frac{\operatorname{P1}(s)}{\operatorname{Q1}(s)}\biggr)
    142 *     ```
    143 *
    144 *     and
    145 *
    146 *     ```tex
    147 *     \operatorname{erfc}(x) = \begin{cases}
    148 *     (1-c) - \frac{\operatorname{P1}(s)}{\operatorname{Q1}(s)} & \textrm{if}\ x > 0 \\
    149 *     1 + \biggl(c + \frac{\operatorname{P1}(s)}{\operatorname{Q1}(s)}\biggr) & \textrm{if}\ x < 0
    150 *     \end{cases}
    151 *     ```
    152 *
    153 *     where
    154 *
    155 *     ```tex
    156 *     \biggl|\frac{\mathrm{P1}}{\mathrm{Q1}} - (\operatorname{erf}(|x|)-c)\biggr| \leq 2^{-59.06}
    157 *     ```
    158 *
    159 *     <!-- <note> -->
    160 *
    161 *     Here, we use the Taylor series expansion at \\(x = 1\\)
    162 *
    163 *     ```tex
    164 *     \begin{align*}
    165 *     \operatorname{erf}(1+s) &= \operatorname{erf}(1) + s\cdot \operatorname{poly}(s) \\
    166 *     &= 0.845.. + \frac{\operatorname{P1}(s)}{\operatorname{Q1}(s)}
    167 *     \end{align*}
    168 *     ```
    169 *
    170 *     using a rational approximation to approximate
    171 *
    172 *     ```tex
    173 *     \operatorname{erf}(1+s) - (c = (\mathrm{single})0.84506291151)
    174 *     ```
    175 *
    176 *     <!-- </note> -->
    177 *
    178 *     Note that, for \\(x \in [0.84375,1.25)\\), \\(|\mathrm{P1}/\mathrm{Q1}| < 0.078\\), where
    179 *
    180 *     -   \\(\operatorname{P1}(s)\\) is a degree \\(6\\) polynomial in \\(s\\)
    181 *     -   \\(\operatorname{Q1}(s)\\) is a degree \\(6\\) polynomial in \\(s\\)
    182 *
    183 * 3.  For \\(x \in [1.25,1/0.35)\\),
    184 *
    185 *     ```tex
    186 *     \begin{align*}
    187 *     \operatorname{erfc}(x) &= \frac{1}{x}e^{-x^2-0.5625+(\mathrm{R1}/\mathrm{S1})} \\
    188 *     \operatorname{erf}(x) &= 1 - \operatorname{erfc}(x)
    189 *     \end{align*}
    190 *     ```
    191 *
    192 *     where
    193 *
    194 *     -   \\(\operatorname{R1}(z)\\) is a degree \\(7\\) polynomial in \\(z\\), where \\(z = 1/x^2\\)
    195 *     -   \\(\operatorname{S1}(z)\\) is a degree \\(8\\) polynomial in \\(z\\)
    196 *
    197 * 4.  For \\(x \in [1/0.35,28)\\),
    198 *
    199 *     ```tex
    200 *     \operatorname{erfc}(x) = \begin{cases}
    201 *     \frac{1}{x} e^{-x^2-0.5625+(\mathrm{R2}/\mathrm{S2})} & \textrm{if}\ x > 0 \\
    202 *     2.0 - \frac{1}{x} e^{-x^2-0.5625+(\mathrm{R2}/\mathrm{S2})} & \textrm{if}\ -6 < x < 0 \\
    203 *     2.0 - \mathrm{tiny} & \textrm{if}\ x \leq -6
    204 *     \end{cases}
    205 *     ```
    206 *
    207 *     and
    208 *
    209 *     ```tex
    210 *     \operatorname{erf}(x) = \begin{cases}
    211 *     \operatorname{sign}(x) \cdot (1.0 - \operatorname{erfc}(x)) & \textrm{if}\ x < 6 \\
    212 *     \operatorname{sign}(x) \cdot (1.0 - \mathrm{tiny}) & \textrm{otherwise}
    213 *     \end{cases}
    214 *     ```
    215 *
    216 *     where
    217 *
    218 *     -   \\(\operatorname{R2}(z)\\) is a degree \\(6\\) polynomial in \\(z\\), where \\(z = 1/x^2\\)
    219 *     -   \\(\operatorname{S2}(z)\\) is a degree \\(7\\) polynomial in \\(z\\)
    220 *
    221 * 5.  For \\(x \in [28, \infty)\\),
    222 *
    223 *     ```tex
    224 *     \begin{align*}
    225 *     \operatorname{erf}(x) &= \operatorname{sign}(x) \cdot (1 - \mathrm{tiny}) & \textrm{(raise inexact)}
    226 *     \end{align*}
    227 *     ```
    228 *
    229 *     and
    230 *
    231 *     ```tex
    232 *     \operatorname{erfc}(x) = \begin{cases}
    233 *     \mathrm{tiny} \cdot \mathrm{tiny} & \textrm{if}\ x > 0\ \textrm{(raise underflow)} \\
    234 *     2 - \mathrm{tiny} & \textrm{if}\ x < 0
    235 *     \end{cases}
    236 *     ```
    237 *
    238 * ## Special Cases
    239 *
    240 * ```tex
    241 * \begin{align*}
    242 * \operatorname{erf}(0) &= 0 \\
    243 * \operatorname{erf}(-0) &= -0 \\
    244 * \operatorname{erf}(\infty) &= 1 \\
    245 * \operatorname{erf}(-\infty) &= -1 \\
    246 * \operatorname{erfc}(0) &= 1 \\
    247 * \operatorname{erfc}(\infty) &= 0 \\
    248 * \operatorname{erfc}(-\infty) &= 2 \\
    249 * \operatorname{erf}(\mathrm{NaN}) &= \mathrm{NaN} \\
    250 * \operatorname{erfc}(\mathrm{NaN}) &= \mathrm{NaN}
    251 * \end{align*}
    252 * ```
    253 *
    254 *
    255 * ## Notes
    256 *
    257 * -   To compute \\(\exp(-x^2-0.5625+(\mathrm{R}/\mathrm{S}))\\), let \\(s\\) be a single precision number and \\(s := x\\); then
    258 *
    259 *     ```tex
    260 *     -x^2 = -s^2 + (s-x)(s+x)
    261 *     ```
    262 *
    263 *     and
    264 *
    265 *     ```tex
    266 *     e^{-x^2-0.5626+(\mathrm{R}/\mathrm{S})} = e^{-s^2-0.5625} e^{(s-x)(s+x)+(\mathrm{R}/\mathrm{S})}
    267 *     ```
    268 *
    269 * -   `#4` and `#5` make use of the asymptotic series
    270 *
    271 *     ```tex
    272 *     \operatorname{erfc}(x) \approx \frac{e^{-x^2}}{x\sqrt{\pi}} (1 + \operatorname{poly}(1/x^2))
    273 *     ```
    274 *
    275 *     We use a rational approximation to approximate
    276 *
    277 *     ```tex
    278 *     g(s) = f(1/x^2) = \ln(\operatorname{erfc}(x) \cdot x) - x^2 + 0.5625
    279 *     ```
    280 *
    281 * -   The error bound for \\(\mathrm{R1}/\mathrm{S1}\\) is
    282 *
    283 *     ```tex
    284 *     |\mathrm{R1}/\mathrm{S1} - f(x)| < 2^{-62.57}
    285 *     ```
    286 *
    287 *     and for \\(\mathrm{R2}/\mathrm{S2}\\) is
    288 *
    289 *     ```tex
    290 *     |\mathrm{R2}/\mathrm{S2} - f(x)| < 2^{-61.52}
    291 *     ```
    292 *
    293 * @param {number} x - input value
    294 * @returns {number} function value
    295 *
    296 * @example
    297 * var y = erf( 2.0 );
    298 * // returns ~0.9953
    299 *
    300 * @example
    301 * var y = erf( -1.0 );
    302 * // returns ~-0.8427
    303 *
    304 * @example
    305 * var y = erf( -0.0 );
    306 * // returns -0.0
    307 *
    308 * @example
    309 * var y = erf( NaN );
    310 * // returns NaN
    311 */
    312 function erf( x ) {
    313 	var sign;
    314 	var ax;
    315 	var z;
    316 	var r;
    317 	var s;
    318 	var y;
    319 	var p;
    320 	var q;
    321 
    322 	// Special case: NaN
    323 	if ( isnan( x ) ) {
    324 		return NaN;
    325 	}
    326 	// Special case: +infinity
    327 	if ( x === PINF ) {
    328 		return 1.0;
    329 	}
    330 	// Special case: -infinity
    331 	if ( x === NINF ) {
    332 		return -1.0;
    333 	}
    334 	// Special case: +-0
    335 	if ( x === 0.0 ) {
    336 		return x;
    337 	}
    338 	if ( x < 0.0 ) {
    339 		sign = true;
    340 		ax = -x;
    341 	} else {
    342 		sign = false;
    343 		ax = x;
    344 	}
    345 	// |x| < 0.84375
    346 	if ( ax < 0.84375 ) {
    347 		if ( ax < SMALL ) {
    348 			if ( ax < VERY_TINY ) {
    349 				// Avoid underflow:
    350 				return 0.125 * ( (8.0*x) + (EFX8*x) );
    351 			}
    352 			return x + (EFX*x);
    353 		}
    354 		z = x * x;
    355 		r = PPC + ( z*polyvalPP( z ) );
    356 		s = QQC + ( z*polyvalQQ( z ) );
    357 		y = r / s;
    358 		return x + (x*y);
    359 	}
    360 	// 0.84375 <= |x| < 1.25
    361 	if ( ax < 1.25 ) {
    362 		s = ax - 1.0;
    363 		p = PAC + ( s*polyvalPA( s ) );
    364 		q = QAC + ( s*polyvalQA( s ) );
    365 		if ( sign ) {
    366 			return -ERX - (p/q);
    367 		}
    368 		return ERX + (p/q);
    369 	}
    370 	// +inf > |x| >= 6
    371 	if ( ax >= 6.0 ) {
    372 		if ( sign ) {
    373 			return TINY - 1.0; // raise inexact
    374 		}
    375 		return 1.0 - TINY; // raise inexact
    376 	}
    377 	s = 1.0 / (ax*ax);
    378 
    379 	// |x| < 1/0.35 ~ 2.857143
    380 	if ( ax < 2.857142857142857 ) {
    381 		r = RAC + ( s*polyvalRA( s ) );
    382 		s = SAC + ( s*polyvalSA( s ) );
    383 	}
    384 	// |x| >= 1/0.35 ~ 2.857143
    385 	else {
    386 		r = RBC + ( s*polyvalRB( s ) );
    387 		s = SBC + ( s*polyvalSB( s ) );
    388 	}
    389 	z = setLowWord( ax, 0 ); // pseudo-single (20-bit) precision x
    390 	r = exp( -(z*z) - 0.5625 ) * exp( ( (z-ax) * (z+ax) ) + (r/s) );
    391 	if ( sign ) {
    392 		return (r/ax) - 1.0;
    393 	}
    394 	return 1.0 - (r/ax);
    395 }
    396 
    397 
    398 // EXPORTS //
    399 
    400 module.exports = erf;