time-to-botec

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

expm1.js (10584B)


      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 [FDLIBM]{@link http://www.netlib.org/fdlibm/s_expm1.c}. The implementation follows the original, but has been modified for JavaScript.
     22 *
     23 * ```text
     24 * Copyright (C) 2004 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 getHighWord = require( '@stdlib/number/float64/base/get-high-word' );
     39 var setHighWord = require( '@stdlib/number/float64/base/set-high-word' );
     40 var PINF = require( '@stdlib/constants/float64/pinf' );
     41 var NINF = require( '@stdlib/constants/float64/ninf' );
     42 var FLOAT64_EXPONENT_BIAS = require( '@stdlib/constants/float64/exponent-bias' );
     43 var HALF_LN2 = require( '@stdlib/constants/float64/half-ln-two' );
     44 var polyval = require( './polyval_q.js' );
     45 
     46 
     47 // VARIABLES //
     48 
     49 var OVERFLOW_THRESHOLD = 7.09782712893383973096e+02; // 0x40862E42 0xFEFA39EF
     50 
     51 // High and low words of ln(2):
     52 var LN2_HI = 6.93147180369123816490e-01; // 0x3FE62E42 0xFEE00000
     53 var LN2_LO = 1.90821492927058770002e-10; // 0x3DEA39EF 0x35793C76
     54 
     55 // 1 / ln(2):
     56 var LN2_INV = 1.44269504088896338700e+00; // 0x3FF71547 0x652B82FE
     57 
     58 // ln(2) * 56:
     59 var LN2x56 = 3.88162421113569373274e+01; // 0x4043687A 0x9F1AF2B1
     60 
     61 // ln(2) * 1.5:
     62 var LN2_HALFX3 = 1.03972077083991796413e+00; // 0x3FF0A2B2 0x3F3BAB73
     63 
     64 
     65 // MAIN //
     66 
     67 /**
     68 * Computes `exp(x) - 1`.
     69 *
     70 * ## Method
     71 *
     72 * 1.  Given \\(x\\), we use argument reduction to find \\(r\\) and an integer \\(k\\) such that
     73 *
     74 *     ```tex
     75 *     x = k \cdot \ln(2) + r
     76 *     ```
     77 *
     78 *     where
     79 *
     80 *     ```tex
     81 *     |r| \leq \frac{\ln(2)}{2} \approx 0.34658
     82 *     ```
     83 *
     84 *     <!-- <note> -->
     85 *
     86 *     A correction term \\(c\\) will need to be computed to compensate for the error in \\(r\\) when rounded to a floating-point number.
     87 *
     88 *     <!-- </note> -->
     89 *
     90 * 2.  To approximate \\(\operatorname{expm1}(r)\\), we use a special rational function on the interval \\(\[0,0.34658]\\). Since
     91 *
     92 *     ```tex
     93 *     r \frac{e^r + 1}{e^r - 1} = 2 + \frac{r^2}{6} - \frac{r^4}{360} + \ldots
     94 *     ```
     95 *
     96 *     we define \\(\operatorname{R1}(r^2)\\) by
     97 *
     98 *     ```tex
     99 *     r \frac{e^r + 1}{e^r - 1} = 2 + \frac{r^2}{6} \operatorname{R1}(r^2)
    100 *     ```
    101 *
    102 *     That is,
    103 *
    104 *     ```tex
    105 *     \begin{align*}
    106 *     \operatorname{R1}(r^2) &= \frac{6}{r} \biggl(\frac{e^r+1}{e^r-1} - \frac{2}{r}\biggr) \\
    107 *     &= \frac{6}{r} \biggl( 1 + 2 \biggl(\frac{1}{e^r-1} - \frac{1}{r}\biggr)\biggr) \\
    108 *     &= 1 - \frac{r^2}{60} + \frac{r^4}{2520} - \frac{r^6}{100800} + \ldots
    109 *     \end{align*}
    110 *     ```
    111 *
    112 *     We use a special Remes algorithm on \\(\[0,0.347]\\) to generate a polynomial of degree \\(5\\) in \\(r^2\\) to approximate \\(\mathrm{R1}\\). The maximum error of this polynomial approximation is bounded by \\(2^{-61}\\). In other words,
    113 *
    114 *     ```tex
    115 *     \operatorname{R1}(z) \approx 1 + \mathrm{Q1} \cdot z + \mathrm{Q2} \cdot z^2 + \mathrm{Q3} \cdot z^3 + \mathrm{Q4} \cdot z^4 + \mathrm{Q5} \cdot z^5
    116 *     ```
    117 *
    118 *     where
    119 *
    120 *     ```tex
    121 *     \begin{align*}
    122 *     \mathrm{Q1} &= -1.6666666666666567384\mbox{e-}2 \\
    123 *     \mathrm{Q2} &= 3.9682539681370365873\mbox{e-}4 \\
    124 *     \mathrm{Q3} &= -9.9206344733435987357\mbox{e-}6 \\
    125 *     \mathrm{Q4} &= 2.5051361420808517002\mbox{e-}7 \\
    126 *     \mathrm{Q5} &= -6.2843505682382617102\mbox{e-}9
    127 *     \end{align*}
    128 *     ```
    129 *
    130 *     where \\(z = r^2\\) and the values of \\(\mathrm{Q1}\\) to \\(\mathrm{Q5}\\) are listed in the source. The error is bounded by
    131 *
    132 *     ```tex
    133 *     \biggl| 1 + \mathrm{Q1} \cdot z + \ldots + \mathrm{Q5} \cdot z - \operatorname{R1}(z) \biggr| \leq 2^{-61}
    134 *     ```
    135 *
    136 *     \\(\operatorname{expm1}(r) = e^r - 1\\) is then computed by the following specific way which minimizes the accumulated rounding error
    137 *
    138 *     ```tex
    139 *     \operatorname{expm1}(r) = r + \frac{r^2}{2} + \frac{r^3}{2} \biggl( \frac{3 - (\mathrm{R1} + \mathrm{R1} \cdot \frac{r}{2})}{6 - r ( 3 - \mathrm{R1} \cdot \frac{r}{2})} \biggr)
    140 *     ```
    141 *
    142 *     To compensate for the error in the argument reduction, we use
    143 *
    144 *     ```tex
    145 *     \begin{align*}
    146 *     \operatorname{expm1}(r+c) &= \operatorname{expm1}(r) + c + \operatorname{expm1}(r) \cdot c \\
    147 *     &\approx \operatorname{expm1}(r) + c + rc
    148 *     \end{align*}
    149 *     ```
    150 *
    151 *     Thus, \\(c + rc\\) will be added in as the correction terms for \\(\operatorname{expm1}(r+c)\\). Now, we can rearrange the term to avoid optimization screw up.
    152 *
    153 *     ```tex
    154 *     \begin{align*}
    155 *     \operatorname{expm1}(r+c) &\approx r - \biggl( \biggl( r + \biggl( \frac{r^2}{2} \biggl( \frac{\mathrm{R1} - (3 - \mathrm{R1} \cdot \frac{r}{2})}{6 - r (3 - \mathrm{R1} \cdot \frac{r}{2})} \biggr) - c \biggr) - c \biggr) - \frac{r^2}{2} \biggr) \\
    156 *     &= r - \mathrm{E}
    157 *     \end{align*}
    158 *     ```
    159 *
    160 * 3.  To scale back to obtain \\(\operatorname{expm1}(x)\\), we have (from step 1)
    161 *
    162 *     ```tex
    163 *     \operatorname{expm1}(x) = \begin{cases}
    164 *     2^k  (\operatorname{expm1}(r) + 1) - 1 \\
    165 *     2^k (\operatorname{expm1}(r) + (1-2^{-k}))
    166 *     \end{cases}
    167 *     ```
    168 *
    169 * ## Special Cases
    170 *
    171 * ```tex
    172 * \begin{align*}
    173 * \operatorname{expm1}(\infty) &= \infty \\
    174 * \operatorname{expm1}(-\infty) &= -1 \\
    175 * \operatorname{expm1}(\mathrm{NaN}) &= \mathrm{NaN}
    176 * \end{align*}
    177 * ```
    178 *
    179 *
    180 * ## Notes
    181 *
    182 * -   For finite arguments, only \\(\operatorname{expm1}(0) = 0\\) is exact.
    183 *
    184 * -   To save one multiplication, we scale the coefficient \\(\mathrm{Qi}\\) to \\(\mathrm{Qi} \cdot {2^i}\\) and replace \\(z\\) by \\(\frac{x^2}{2}\\).
    185 *
    186 * -   To achieve maximum accuracy, we compute \\(\operatorname{expm1}(x)\\) by
    187 *
    188 *     -   if \\(x < -56 \cdot \ln(2)\\), return \\(-1.0\\) (raise inexact if \\(x\\) does not equal \\(\infty\\))
    189 *
    190 *     -   if \\(k = 0\\), return \\(r-\mathrm{E}\\)
    191 *
    192 *     -   if \\(k = -1\\), return \\(\frac{(r-\mathrm{E})-1}{2}\\)
    193 *
    194 *     -   if \\(k = 1\\),
    195 *
    196 *         -   if \\(r < -0.25\\), return \\(2((r+0.5)- \mathrm{E})\\)
    197 *         -   else return \\(1+2(r-\mathrm{E})\\)
    198 *
    199 *     -   if \\(k < -2\\) or \\(k > 56\\), return \\(2^k(1-(\mathrm{E}-r)) - 1\\) (or \\(e^x-1\\))
    200 *
    201 *     -   if \\(k \leq 20\\), return \\(2^k((1-2^{-k})-(\mathrm{E}-r))\\)
    202 *
    203 *     -   else return \\(2^k(1-((\mathrm{E}+2^{-k})-r))\\)
    204 *
    205 * -   For IEEE 754 double, if \\(x > 7.09782712893383973096\mbox{e+}02\\), then \\(\operatorname{expm1}(x)\\) will overflow.
    206 *
    207 * -   The hexadecimal values listed in the source are the intended ones for the implementation constants. Decimal values may be used, provided that the compiler will convert from decimal to binary accurately enough to produce the intended hexadecimal values.
    208 *
    209 * -   According to an error analysis, the error is always less than \\(1\\) ulp (unit in the last place).
    210 *
    211 *
    212 * @param {number} x - input value
    213 * @returns {number} function value
    214 *
    215 * @example
    216 * var v = expm1( 0.2 );
    217 * // returns ~0.221
    218 *
    219 * @example
    220 * var v = expm1( -9.0 );
    221 * // returns ~-0.9999
    222 *
    223 * @example
    224 * var v = expm1( 0.0 );
    225 * // returns 0.0
    226 *
    227 * @example
    228 * var v = expm1( NaN );
    229 * // returns NaN
    230 */
    231 function expm1( x ) {
    232 	var halfX;
    233 	var sign;
    234 	var hi;
    235 	var lo;
    236 	var hx;
    237 	var r1;
    238 	var y;
    239 	var z;
    240 	var c;
    241 	var t;
    242 	var e;
    243 	var k;
    244 
    245 	if ( x === PINF || isnan( x ) ) {
    246 		return x;
    247 	}
    248 	if ( x === NINF ) {
    249 		return -1.0;
    250 	}
    251 	if ( x === 0.0 ) {
    252 		return x; // handles +-0 (IEEE 754-2008)
    253 	}
    254 	// Set y = |x|:
    255 	if ( x < 0.0 ) {
    256 		sign = true;
    257 		y = -x;
    258 	} else {
    259 		sign = false;
    260 		y = x;
    261 	}
    262 	// Filter out huge and non-finite arguments...
    263 	if ( y >= LN2x56 ) { // if |x| >= 56*ln(2)
    264 		if ( sign ) { // if x <= -56*ln(2)
    265 			return -1.0;
    266 		}
    267 		if ( y >= OVERFLOW_THRESHOLD ) { // if |x| >= 709.78...
    268 			return PINF;
    269 		}
    270 	}
    271 	// Extract the more significant bits from |x|:
    272 	hx = getHighWord( y )|0; // asm type annotation
    273 
    274 	// Argument reduction...
    275 	if ( y > HALF_LN2 ) { // if |x| > 0.5*ln(2)
    276 		if ( y < LN2_HALFX3 ) { // if |x| < 1.5*ln(2)
    277 			if ( sign ) {
    278 				hi = x + LN2_HI;
    279 				lo = -LN2_LO;
    280 				k = -1;
    281 			} else {
    282 				hi = x - LN2_HI;
    283 				lo = LN2_LO;
    284 				k = 1;
    285 			}
    286 		} else {
    287 			if ( sign ) {
    288 				k = (LN2_INV*x) - 0.5;
    289 			} else {
    290 				k = (LN2_INV*x) + 0.5;
    291 			}
    292 			k |= 0; // use a bitwise OR to cast `k` to an integer (see also asm.js type annotations: http://asmjs.org/spec/latest/#annotations)
    293 			t = k;
    294 			hi = x - (t*LN2_HI); // t*ln2_hi is exact here
    295 			lo = t * LN2_LO;
    296 		}
    297 		x = hi - lo;
    298 		c = (hi-x) - lo;
    299 	}
    300 	// if |x| < 2**-54 => high word: 0 01111001001 00000000000000000000 => 0x3c900000 = 1016070144  => exponent = 01111001001 = 969 = 1023-54
    301 	else if ( hx < 1016070144 ) {
    302 		return x;
    303 	}
    304 	else {
    305 		k = 0;
    306 	}
    307 	// x is now in primary range...
    308 	halfX = 0.5 * x;
    309 	z = x * halfX;
    310 
    311 	r1 = 1.0 + ( z * polyval( z ) );
    312 
    313 	t = 3.0 - (r1*halfX);
    314 	e = z * ( (r1-t) / (6.0 - (x*t)) );
    315 	if ( k === 0 ) {
    316 		return x - ( (x*e) - z );	// c is 0
    317 	}
    318 	e = ( x * (e-c) ) - c;
    319 	e -= z;
    320 	if ( k === -1 ) {
    321 		return ( 0.5*(x-e) )- 0.5;
    322 	}
    323 	if ( k === 1 ) {
    324 		if ( x < -0.25 ) {
    325 			return -2.0 * ( e - (x+0.5) );
    326 		}
    327 		return 1 + ( 2.0 * (x-e) );
    328 	}
    329 	if ( k <= -2 || k > 56 ) { // suffice to return exp(x)-1
    330 		y = 1.0 - (e-x);
    331 
    332 		// Add k to y's exponent:
    333 		hi = (getHighWord( y ) + (k<<20))|0; // asm type annotation
    334 		y = setHighWord( y, hi );
    335 
    336 		return y - 1.0;
    337 	}
    338 	t = 1.0;
    339 	if ( k < 20 ) {
    340 		// 0x3ff00000 - (0x200000>>k) = 1072693248 - (0x200000>>k) => 0x200000 = 0 00000000010 00000000000000000000
    341 		hi = (1072693248 - (0x200000>>k))|0; // asm type annotation
    342 		t = setHighWord( t, hi ); // t=1-2^-k
    343 		y = t - (e-x);
    344 	} else {
    345 		hi = ( (FLOAT64_EXPONENT_BIAS-k)<<20 )|0; // asm type annotation
    346 		t = setHighWord( t, hi ); // t=2^-k
    347 		y = x - (e+t);
    348 		y += 1.0;
    349 	}
    350 	// Add k to y's exponent:
    351 	hi = (getHighWord( y ) + (k<<20))|0; // asm type annotation
    352 	return setHighWord( y, hi );
    353 }
    354 
    355 
    356 // EXPORTS //
    357 
    358 module.exports = expm1;