log1p.js (9445B)
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_log1p.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 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 polyval = require( './polyval_lp.js' ); 44 45 46 // VARIABLES // 47 48 // High and low words of ln(2): 49 var LN2_HI = 6.93147180369123816490e-01; // 0x3fe62e42 0xfee00000 50 var LN2_LO = 1.90821492927058770002e-10; // 0x3dea39ef 0x35793c76 51 52 // sqrt(2)-1: 53 var SQRT2M1 = 4.142135623730950488017e-01; // 0x3fda8279 0x99fcef34 54 55 // sqrt(2)/2-1: 56 var SQRT2HALFM1 = -2.928932188134524755992e-01; // 0xbfd2bec3 0x33018866 57 58 // 2**-29: 59 var SMALL = 1.862645149230957e-09; // 0x3e200000 0x00000000 60 61 // 2**-54: 62 var TINY = 5.551115123125783e-17; 63 64 // Max integer (unsafe) => 2**53: 65 var TWO53 = 9007199254740992; 66 67 // 2/3: 68 var TWO_THIRDS = 6.666666666666666666e-01; 69 70 71 // MAIN // 72 73 /** 74 * Evaluates the natural logarithm of \\(1+x\\). 75 * 76 * ## Method 77 * 78 * 1. Argument Reduction: find \\(k\\) and \\(f\\) such that 79 * 80 * ```tex 81 * 1+x = 2^k (1+f) 82 * ``` 83 * 84 * where 85 * 86 * ```tex 87 * \frac{\sqrt{2}}{2} < 1+f < \sqrt{2} 88 * ``` 89 * 90 * <!-- <note> --> 91 * 92 * If \\(k=0\\), then \\(f=x\\) is exact. However, if \\(k \neq 0\\), then \\(f\\) may not be representable exactly. In that case, a correction term is needed. Let 93 * 94 * ```tex 95 * u = \operatorname{round}(1+x) 96 * ``` 97 * 98 * and 99 * 100 * ```tex 101 * c = (1+x) - u 102 * ``` 103 * 104 * then 105 * 106 * ```tex 107 * \ln (1+x) - \ln u \approx \frac{c}{u} 108 * ``` 109 * 110 * We can thus proceed to compute \\(\ln(u)\\), and add back the correction term \\(c/u\\). 111 * 112 * <!-- </note> --> 113 * 114 * <!-- <note> --> 115 * 116 * When \\(x > 2^{53}\\), one can simply return \\(\ln(x)\\). 117 * 118 * <!-- </note> --> 119 * 120 * 2. Approximation of \\(\operatorname{log1p}(f)\\). Let 121 * 122 * ```tex 123 * s = \frac{f}{2+f} 124 * ``` 125 * 126 * based on 127 * 128 * ```tex 129 * \begin{align*} 130 * \ln 1+f &= \ln (1+s) - \ln (1-s) \\ 131 * &= 2s + \frac{2}{3} s^3 + \frac{2}{5} s^5 + ... \\ 132 * &= 2s + sR \\ 133 * \end{align*} 134 * ``` 135 * 136 * We use a special Reme algorithm on \\(\[0,0.1716\]\\) to generate a polynomial of degree \\(14\\) to approximate \\(R\\). The maximum error of this polynomial approximation is bounded by \\(2^{-58.45}\\). In other words, 137 * 138 * ```tex 139 * R(z) \approx \mathrm{Lp}_1 s^2 + \mathrm{Lp}_2 s^4 + \mathrm{Lp}_3 s^6 + \mathrm{Lp}_4 s^8 + \mathrm{Lp}_5 s^{10} + \mathrm{Lp}_6 s^{12} + \mathrm{Lp}_7 s^{14} 140 * ``` 141 * 142 * and 143 * 144 * ```tex 145 * | \mathrm{Lp}_1 s^2 + \ldots + \mathrm{Lp}_7 s^14 - R(z) | \leq 2^{-58.45} 146 * ``` 147 * 148 * <!-- <note> --> 149 * 150 * The values of \\(Lp1\\) to \\(Lp7\\) may be found in the source. 151 * 152 * <!-- </note> --> 153 * 154 * Note that 155 * 156 * ```tex 157 * \begin{align*} 158 * 2s &= f - sf \\ 159 * &= f - \frac{f^2}{2} + s \frac{f^2}{2} \\ 160 * \end{align*} 161 * ``` 162 * 163 * In order to guarantee error in \\(\ln\\) below \\(1\ \mathrm{ulp}\\), we compute the log by 164 * 165 * ```tex 166 * \operatorname{log1p}(f) = f - \biggl(\frac{f^2}{2} - s\biggl(\frac{f^2}{2}+R\biggr)\biggr) 167 * ``` 168 * 169 * 3. Finally, 170 * 171 * ```tex 172 * \begin{align*} 173 * \operatorname{log1p}(x) &= k \cdot \mathrm{ln2} + \operatorname{log1p}(f) \\ 174 * &= k \cdot \mathrm{ln2}_{hi}+\biggl(f-\biggl(\frac{f^2}{2}-\biggl(s\biggl(\frac{f^2}{2}+R\biggr)+k \cdot \mathrm{ln2}_{lo}\biggr)\biggr)\biggr) \\ 175 * \end{align*} 176 * ``` 177 * 178 * Here \\(\mathrm{ln2}\\) is split into two floating point numbers: 179 * 180 * ```tex 181 * \mathrm{ln2}_{hi} + \mathrm{ln2}_{lo} 182 * ``` 183 * 184 * where \\(n \cdot \mathrm{ln2}_{hi}\\) is always exact for \\(|n| < 2000\\). 185 * 186 * 187 * ## Special Cases 188 * 189 * - \\(\operatorname{log1p}(x) = \mathrm{NaN}\\) with signal if \\(x < -1\\) (including \\(-\infty\\)) 190 * - \\(\operatorname{log1p}(+\infty) = +\infty\\) 191 * - \\(\operatorname{log1p}(-1) = -\infty\\) with signal 192 * - \\(\operatorname{log1p}(\mathrm{NaN})= \mathrm{NaN}\\) with no signal 193 * 194 * 195 * ## Notes 196 * 197 * - According to an error analysis, the error is always less than \\(1\\) ulp (unit in the last place). 198 * 199 * - The hexadecimal values are the intended ones for the used constants. The decimal values may be used, provided that the compiler will convert from decimal to binary accurately enough to produce the hexadecimal values shown. 200 * 201 * - Assuming \\(\ln(x)\\) is accurate, the following algorithm can be used to evaluate \\(\operatorname{log1p}(x)\\) to within a few ULP: 202 * 203 * ```javascript 204 * var u = 1.0 + x; 205 * if ( u === 1.0 ) { 206 * return x; 207 * } else { 208 * return ln(u) * (x/(u-1.0)); 209 * } 210 * ``` 211 * 212 * See HP-15C Advanced Functions Handbook, p.193. 213 * 214 * 215 * @param {number} x - input value 216 * @returns {number} the natural logarithm of `1+x` 217 * 218 * @example 219 * var v = log1p( 4.0 ); 220 * // returns ~1.609 221 * 222 * @example 223 * var v = log1p( -1.0 ); 224 * // returns -Infinity 225 * 226 * @example 227 * var v = log1p( 0.0 ); 228 * // returns 0.0 229 * 230 * @example 231 * var v = log1p( -0.0 ); 232 * // returns -0.0 233 * 234 * @example 235 * var v = log1p( -2.0 ); 236 * // returns NaN 237 * 238 * @example 239 * var v = log1p( NaN ); 240 * // returns NaN 241 */ 242 function log1p( x ) { 243 var hfsq; 244 var hu; 245 var y; 246 var f; 247 var c; 248 var s; 249 var z; 250 var R; 251 var u; 252 var k; 253 254 if ( x < -1.0 || isnan( x ) ) { 255 return NaN; 256 } 257 if ( x === -1.0 ) { 258 return NINF; 259 } 260 if ( x === PINF ) { 261 return x; 262 } 263 if ( x === 0.0 ) { 264 return x; // handle +-0 (IEEE 754-2008 spec) 265 } 266 // Set y = |x|: 267 if ( x < 0.0 ) { 268 y = -x; 269 } else { 270 y = x; 271 } 272 // Argument reduction... 273 k = 1; 274 275 // Check if argument reduction is needed and if we can just return a small value approximation requiring less computation but with equivalent accuracy... 276 if ( y < SQRT2M1 ) { // if |x| < sqrt(2)-1 => ~0.41422 277 if ( y < SMALL ) { // if |x| < 2**-29 278 if ( y < TINY ) { // if |x| < 2**-54 279 return x; 280 } 281 // Use a simple two-term Taylor series... 282 return x - ( x*x*0.5 ); 283 } 284 // Check if `f=x` can be represented exactly (no need for correction terms), allowing us to bypass argument reduction... 285 if ( x > SQRT2HALFM1 ) { // if x > sqrt(2)/2-1 => ~-0.2929 286 // -0.2929 < x < 0.41422 287 k = 0; 288 f = x; // exact 289 hu = 1; 290 } 291 } 292 // Address case where `f` cannot be represented exactly... 293 if ( k !== 0 ) { 294 if ( y < TWO53 ) { 295 u = 1.0 + x; 296 hu = getHighWord( u ); 297 298 // Bit shift to isolate the exponent and then subtract the bias: 299 k = (hu>>20) - FLOAT64_EXPONENT_BIAS; 300 301 // Correction term... 302 if ( k > 0 ) { // positive unbiased exponent 303 c = 1.0 - (u-x); 304 } else { // nonpositive unbiased exponent 305 c = x - (u-1.0); 306 } 307 c /= u; 308 } else { 309 u = x; 310 hu = getHighWord( u ); 311 312 // Bit shift to isolate the exponent and then subtract the bias: 313 k = (hu>>20) - FLOAT64_EXPONENT_BIAS; 314 315 // Correction term is zero: 316 c = 0; 317 } 318 // Apply a bit mask (0 00000000000 11111111111111111111) to remove the exponent: 319 hu &= 0x000fffff; // max value => 1048575 320 321 // Check if u significand is less than sqrt(2) significand => 0x6a09e => 01101010000010011110 322 if ( hu < 434334 ) { 323 // Normalize u by setting the exponent to 1023 (bias) => 0x3ff00000 => 0 01111111111 00000000000000000000 324 u = setHighWord( u, hu|0x3ff00000 ); 325 } else { 326 k += 1; 327 328 // Normalize u/2 by setting the exponent to 1022 (bias-1 => 2**-1 = 1/2) => 0x3fe00000 => 0 01111111110 00000000000000000000 329 u = setHighWord( u, hu|0x3fe00000 ); 330 331 // Subtract hu significand from next largest hu => 0 00000000001 00000000000000000000 => 0x00100000 => 1048576 332 hu = (1048576-hu)>>2; 333 } 334 f = u - 1.0; 335 } 336 // Approximation of log1p(f)... 337 hfsq = 0.5 * f * f; 338 if ( hu === 0 ) { // if |f| < 2**-20 339 if ( f === 0.0 ) { 340 c += k * LN2_LO; 341 return ( k * LN2_HI ) + c; 342 } 343 R = hfsq * (1.0 - ( TWO_THIRDS*f ) ); // avoid division 344 return ( k*LN2_HI ) - ( (R - ( (k*LN2_LO) + c)) - f ); 345 } 346 s = f / (2.0 + f); 347 z = s * s; 348 349 R = z * polyval( z ); 350 351 if ( k === 0 ) { 352 return f - ( hfsq - ( s*(hfsq+R) ) ); 353 } 354 return ( k*LN2_HI ) - ( (hfsq - ( (s*(hfsq+R)) + ((k*LN2_LO) + c))) - f ); 355 } 356 357 358 // EXPORTS // 359 360 module.exports = log1p;