time-to-botec

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

erfcinv.js (6126B)


      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 original C++ code and copyright notice are from the [Boost library]{@link http://www.boost.org/doc/libs/1_48_0/boost/math/special_functions/detail/erf_inv.hpp}. This implementation follows the original, but has been modified for JavaScript.
     22 *
     23 * ```text
     24 * (C) Copyright John Maddock 2006.
     25 *
     26 * Use, modification and distribution are subject to the
     27 * Boost Software License, Version 1.0. (See accompanying file
     28 * LICENSE or copy at http://www.boost.org/LICENSE_1_0.txt)
     29 * ```
     30 */
     31 
     32 'use strict';
     33 
     34 // MODULES //
     35 
     36 var isnan = require( './../../../../base/assert/is-nan' );
     37 var sqrt = require( './../../../../base/special/sqrt' );
     38 var ln = require( './../../../../base/special/ln' );
     39 var PINF = require( '@stdlib/constants/float64/pinf' );
     40 var NINF = require( '@stdlib/constants/float64/ninf' );
     41 var rationalFcnR1 = require( './rational_p1q1.js' );
     42 var rationalFcnR2 = require( './rational_p2q2.js' );
     43 var rationalFcnR3 = require( './rational_p3q3.js' );
     44 var rationalFcnR4 = require( './rational_p4q4.js' );
     45 var rationalFcnR5 = require( './rational_p5q5.js' );
     46 
     47 
     48 // VARIABLES //
     49 
     50 var Y1 = 8.91314744949340820313e-2;
     51 var Y2 = 2.249481201171875;
     52 var Y3 = 8.07220458984375e-1;
     53 var Y4 = 9.3995571136474609375e-1;
     54 var Y5 = 9.8362827301025390625e-1;
     55 
     56 
     57 // MAIN //
     58 
     59 /**
     60 * Evaluates the inverse complementary error function.
     61 *
     62 * Note that
     63 *
     64 * ```tex
     65 * \operatorname{erfc^{-1}}(1-z) = \operatorname{erf^{-1}}(z)
     66 * ```
     67 *
     68 * ## Method
     69 *
     70 * 1.  For \\(|x| \leq 0.5\\), we evaluate the inverse error function using the rational approximation
     71 *
     72 *     ```tex
     73 *     \operatorname{erf^{-1}}(x) = x(x+10)(\mathrm{Y} + \operatorname{R}(x))
     74 *     ```
     75 *
     76 *     where \\(Y\\) is a constant and \\(\operatorname{R}(x)\\) is optimized for a low absolute error compared to \\(|Y|\\).
     77 *
     78 *     <!-- <note> -->
     79 *
     80 *     Max error \\(2.001849\mbox{e-}18\\). Maximum deviation found (error term at infinite precision) \\(8.030\mbox{e-}21\\).
     81 *
     82 *     <!-- </note> -->
     83 *
     84 * 2.  For \\(0.5 > 1-|x| \geq 0\\), we evaluate the inverse error function using the rational approximation
     85 *
     86 *     ```tex
     87 *     \operatorname{erf^{-1}} = \frac{\sqrt{-2 \cdot \ln(1-x)}}{\mathrm{Y} + \operatorname{R}(1-x)}
     88 *     ```
     89 *
     90 *     where \\(Y\\) is a constant, and \\(\operatorname{R}(q)\\) is optimized for a low absolute error compared to \\(Y\\).
     91 *
     92 *     <!-- <note> -->
     93 *
     94 *     Max error \\(7.403372\mbox{e-}17\\). Maximum deviation found (error term at infinite precision) \\(4.811\mbox{e-}20\\).
     95 *
     96 *     <!-- </note> -->
     97 *
     98 * 3.  For \\(1-|x| < 0.25\\), we have a series of rational approximations all of the general form
     99 *
    100 *     ```tex
    101 *     p = \sqrt{-\ln(1-x)}
    102 *     ```
    103 *
    104 *     Accordingly, the result is given by
    105 *
    106 *     ```tex
    107 *     \operatorname{erf^{-1}}(x) = p(\mathrm{Y} + \operatorname{R}(p-B))
    108 *     ```
    109 *
    110 *     where \\(Y\\) is a constant, \\(B\\) is the lowest value of \\(p\\) for which the approximation is valid, and \\(\operatorname{R}(x-B)\\) is optimized for a low absolute error compared to \\(Y\\).
    111 *
    112 *     <!-- <note> -->
    113 *
    114 *     Almost all code will only go through the first or maybe second approximation.  After that we are dealing with very small input values.
    115 *
    116 *     -   If \\(p < 3\\), max error \\(1.089051\mbox{e-}20\\).
    117 *     -   If \\(p < 6\\), max error \\(8.389174\mbox{e-}21\\).
    118 *     -   If \\(p < 18\\), max error \\(1.481312\mbox{e-}19\\).
    119 *     -   If \\(p < 44\\), max error \\(5.697761\mbox{e-}20\\).
    120 *     -   If \\(p \geq 44\\), max error \\(1.279746\mbox{e-}20\\).
    121 *
    122 *     <!-- </note> -->
    123 *
    124 *     <!-- <note> -->
    125 *
    126 *     The Boost library can accommodate \\(80\\) and \\(128\\) bit long doubles. JavaScript only supports a \\(64\\) bit double (IEEE 754). Accordingly, the smallest \\(p\\) (in JavaScript at the time of this writing) is \\(\sqrt{-\ln(\sim5\mbox{e-}324)} = 27.284429111150214\\).
    127 *
    128 *     <!-- </note> -->
    129 *
    130 *
    131 * @param {number} x - input value
    132 * @returns {number} function value
    133 *
    134 * @example
    135 * var y = erfcinv( 0.5 );
    136 * // returns ~0.4769
    137 *
    138 * @example
    139 * var y = erfcinv( 0.8 );
    140 * // returns ~0.1791
    141 *
    142 * @example
    143 * var y = erfcinv( 0.0 );
    144 * // returns Infinity
    145 *
    146 * @example
    147 * var y = erfcinv( 2.0 );
    148 * // returns -Infinity
    149 *
    150 * @example
    151 * var y = erfcinv( NaN );
    152 * // returns NaN
    153 */
    154 function erfcinv( x ) {
    155 	var sign;
    156 	var qs;
    157 	var q;
    158 	var g;
    159 	var r;
    160 
    161 	// Special case: NaN
    162 	if ( isnan( x ) ) {
    163 		return NaN;
    164 	}
    165 	// Special case: 0
    166 	if ( x === 0.0 ) {
    167 		return PINF;
    168 	}
    169 	// Special case: 2
    170 	if ( x === 2.0 ) {
    171 		return NINF;
    172 	}
    173 	// Special case: 1
    174 	if ( x === 1.0 ) {
    175 		return 0.0;
    176 	}
    177 	if ( x > 2.0 || x < 0.0 ) {
    178 		return NaN;
    179 	}
    180 	// Argument reduction (reduce to interval [0,1]). If `x` is outside [0,1], we can take advantage of the complementary error function reflection formula: `erfc(-z) = 2 - erfc(z)`, by negating the result once finished.
    181 	if ( x > 1.0 ) {
    182 		sign = -1.0;
    183 		q = 2.0 - x;
    184 	} else {
    185 		sign = 1.0;
    186 		q = x;
    187 	}
    188 	x = 1.0 - q;
    189 
    190 	// x = 1-q <= 0.5
    191 	if ( x <= 0.5 ) {
    192 		g = x * ( x + 10.0 );
    193 		r = rationalFcnR1( x );
    194 		return sign * ( (g*Y1) + (g*r) );
    195 	}
    196 	// q >= 0.25
    197 	if ( q >= 0.25 ) {
    198 		g = sqrt( -2.0 * ln(q) );
    199 		q -= 0.25;
    200 		r = rationalFcnR2( q );
    201 		return sign * ( g / (Y2+r) );
    202 	}
    203 	q = sqrt( -ln( q ) );
    204 
    205 	// q < 3
    206 	if ( q < 3.0 ) {
    207 		qs = q - 1.125;
    208 		r = rationalFcnR3( qs );
    209 		return sign * ( (Y3*q) + (r*q) );
    210 	}
    211 	// q < 6
    212 	if ( q < 6.0 ) {
    213 		qs = q - 3.0;
    214 		r = rationalFcnR4( qs );
    215 		return sign * ( (Y4*q) + (r*q) );
    216 	}
    217 	// q < 18
    218 	qs = q - 6.0;
    219 	r = rationalFcnR5( qs );
    220 	return sign * ( (Y5*q) + (r*q) );
    221 }
    222 
    223 
    224 // EXPORTS //
    225 
    226 module.exports = erfcinv;