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;