pow.js (10177B)
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 and license 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_pow.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 isOdd = require( './../../../../base/assert/is-odd' ); 39 var isInfinite = require( './../../../../base/assert/is-infinite' ); 40 var isInteger = require( './../../../../base/assert/is-integer' ); 41 var sqrt = require( './../../../../base/special/sqrt' ); 42 var abs = require( './../../../../base/special/abs' ); 43 var toWords = require( '@stdlib/number/float64/base/to-words' ); 44 var setLowWord = require( '@stdlib/number/float64/base/set-low-word' ); 45 var uint32ToInt32 = require( '@stdlib/number/uint32/base/to-int32' ); 46 var NINF = require( '@stdlib/constants/float64/ninf' ); 47 var PINF = require( '@stdlib/constants/float64/pinf' ); 48 var xIsZero = require( './x_is_zero.js' ); 49 var yIsHuge = require( './y_is_huge.js' ); 50 var yIsInfinite = require( './y_is_infinite.js' ); 51 var log2ax = require( './log2ax.js' ); 52 var logx = require( './logx.js' ); 53 var pow2 = require( './pow2.js' ); 54 55 56 // VARIABLES // 57 58 // 0x7fffffff = 2147483647 => 0 11111111111 11111111111111111111 59 var ABS_MASK = 0x7fffffff|0; // asm type annotation 60 61 // 0x3fefffff = 1072693247 => 0 01111111110 11111111111111111111 => biased exponent: 1022 = -1+1023 => 2^-1 62 var HIGH_MAX_NEAR_UNITY = 0x3fefffff|0; // asm type annotation 63 64 // 0x41e00000 = 1105199104 => 0 10000011110 00000000000000000000 => biased exponent: 1054 = 31+1023 => 2^31 65 var HIGH_BIASED_EXP_31 = 0x41e00000|0; // asm type annotation 66 67 // 0x43f00000 = 1139802112 => 0 10000111111 00000000000000000000 => biased exponent: 1087 = 64+1023 => 2^64 68 var HIGH_BIASED_EXP_64 = 0x43f00000|0; // asm type annotation 69 70 // 0x40900000 = 1083179008 => 0 10000001001 00000000000000000000 => biased exponent: 1033 = 10+1023 => 2^10 = 1024 71 var HIGH_BIASED_EXP_10 = 0x40900000|0; // asm type annotation 72 73 // 0x3ff00000 = 1072693248 => 0 01111111111 00000000000000000000 => biased exponent: 1023 = 0+1023 => 2^0 = 1 74 var HIGH_BIASED_EXP_0 = 0x3ff00000|0; // asm type annotation 75 76 // 0x4090cc00 = 1083231232 => 0 10000001001 00001100110000000000 77 var HIGH_1075 = 0x4090cc00|0; // asm type annotation 78 79 // 0xc090cc00 = 3230714880 => 1 10000001001 00001100110000000000 80 var HIGH_NEG_1075 = 0xc090cc00>>>0; // asm type annotation 81 82 var HIGH_NUM_NONSIGN_BITS = 31|0; // asm type annotation 83 84 var HUGE = 1.0e300; 85 var TINY = 1.0e-300; 86 87 // -(1024-log2(ovfl+.5ulp)) 88 var OVT = 8.0085662595372944372e-17; 89 90 // High/low words workspace: 91 var WORDS = [ 0|0, 0|0 ]; // WARNING: not thread safe 92 93 // Log workspace: 94 var LOG_WORKSPACE = [ 0.0, 0.0 ]; // WARNING: not thread safe 95 96 97 // MAIN // 98 99 /** 100 * Evaluates the exponential function. 101 * 102 * ## Method 103 * 104 * 1. Let \\(x = 2^n (1+f)\\). 105 * 106 * 2. Compute \\(\operatorname{log2}(x)\\) as 107 * 108 * ```tex 109 * \operatorname{log2}(x) = w_1 + w_2 110 * ``` 111 * 112 * where \\(w_1\\) has \\(53 - 24 = 29\\) bit trailing zeros. 113 * 114 * 3. Compute 115 * 116 * ```tex 117 * y \cdot \operatorname{log2}(x) = n + y^\prime 118 * ``` 119 * 120 * by simulating multi-precision arithmetic, where \\(|y^\prime| \leq 0.5\\). 121 * 122 * 4. Return 123 * 124 * ```tex 125 * x^y = 2^n e^{y^\prime \cdot \mathrm{log2}} 126 * ``` 127 * 128 * ## Special Cases 129 * 130 * ```tex 131 * \begin{align*} 132 * x^{\mathrm{NaN}} &= \mathrm{NaN} & \\ 133 * (\mathrm{NaN})^y &= \mathrm{NaN} & \\ 134 * 1^y &= 1 & \\ 135 * x^0 &= 1 & \\ 136 * x^1 &= x & \\ 137 * (\pm 0)^\infty &= +0 & \\ 138 * (\pm 0)^{-\infty} &= +\infty & \\ 139 * (+0)^y &= +0 & \mathrm{if}\ y > 0 \\ 140 * (+0)^y &= +\infty & \mathrm{if}\ y < 0 \\ 141 * (-0)^y &= -\infty & \mathrm{if}\ y\ \mathrm{is\ an\ odd\ integer\ and}\ y < 0 \\ 142 * (-0)^y &= +\infty & \mathrm{if}\ y\ \mathrm{is\ not\ an\ odd\ integer\ and}\ y < 0 \\ 143 * (-0)^y &= -0 & \mathrm{if}\ y\ \mathrm{is\ an\ odd\ integer\ and}\ y > 0 \\ 144 * (-0)^y &= +0 & \mathrm{if}\ y\ \mathrm{is\ not\ an\ odd\ integer\ and}\ y > 0 \\ 145 * (-1)^{\pm\infty} &= \mathrm{NaN} & \\ 146 * x^{\infty} &= +\infty & |x| > 1 \\ 147 * x^{\infty} &= +0 & |x| < 1 \\ 148 * x^{-\infty} &= +0 & |x| > 1 \\ 149 * x^{-\infty} &= +\infty & |x| < 1 \\ 150 * (-\infty)^y &= (-0)^y & \\ 151 * \infty^y &= +0 & y < 0 \\ 152 * \infty^y &= +\infty & y > 0 \\ 153 * x^y &= \mathrm{NaN} & \mathrm{if}\ y\ \mathrm{is\ not\ a\ finite\ integer\ and}\ x < 0 154 * \end{align*} 155 * ``` 156 * 157 * ## Notes 158 * 159 * - \\(\operatorname{pow}(x,y)\\) returns \\(x^y\\) nearly rounded. In particular, \\(\operatorname{pow}(<\mathrm{integer}>,<\mathrm{integer}>)\\) **always** returns the correct integer, provided the value is representable. 160 * - The hexadecimal values shown in the source code are the intended values for used constants. Decimal values may be used, provided the compiler will accurately convert decimal to binary in order to produce the hexadecimal values. 161 * 162 * 163 * @param {number} x - base 164 * @param {number} y - exponent 165 * @returns {number} function value 166 * 167 * @example 168 * var v = pow( 2.0, 3.0 ); 169 * // returns 8.0 170 * 171 * @example 172 * var v = pow( 4.0, 0.5 ); 173 * // returns 2.0 174 * 175 * @example 176 * var v = pow( 100.0, 0.0 ); 177 * // returns 1.0 178 * 179 * @example 180 * var v = pow( 3.141592653589793, 5.0 ); 181 * // returns ~306.0197 182 * 183 * @example 184 * var v = pow( 3.141592653589793, -0.2 ); 185 * // returns ~0.7954 186 * 187 * @example 188 * var v = pow( NaN, 3.0 ); 189 * // returns NaN 190 * 191 * @example 192 * var v = pow( 5.0, NaN ); 193 * // returns NaN 194 * 195 * @example 196 * var v = pow( NaN, NaN ); 197 * // returns NaN 198 */ 199 function pow( x, y ) { 200 var ahx; // absolute value high word `x` 201 var ahy; // absolute value high word `y` 202 var ax; // absolute value `x` 203 var hx; // high word `x` 204 var lx; // low word `x` 205 var hy; // high word `y` 206 var ly; // low word `y` 207 var sx; // sign `x` 208 var sy; // sign `y` 209 var y1; 210 var hp; 211 var lp; 212 var t; 213 var z; // y prime 214 var j; 215 var i; 216 if ( isnan( x ) || isnan( y ) ) { 217 return NaN; 218 } 219 // Split `y` into high and low words: 220 toWords( WORDS, y ); 221 hy = WORDS[ 0 ]; 222 ly = WORDS[ 1 ]; 223 224 // Special cases `y`... 225 if ( ly === 0 ) { 226 if ( y === 0.0 ) { 227 return 1.0; 228 } 229 if ( y === 1.0 ) { 230 return x; 231 } 232 if ( y === -1.0 ) { 233 return 1.0 / x; 234 } 235 if ( y === 0.5 ) { 236 return sqrt( x ); 237 } 238 if ( y === -0.5 ) { 239 return 1.0 / sqrt( x ); 240 } 241 if ( y === 2.0 ) { 242 return x * x; 243 } 244 if ( y === 3.0 ) { 245 return x * x * x; 246 } 247 if ( y === 4.0 ) { 248 x *= x; 249 return x * x; 250 } 251 if ( isInfinite( y ) ) { 252 return yIsInfinite( x, y ); 253 } 254 } 255 // Split `x` into high and low words: 256 toWords( WORDS, x ); 257 hx = WORDS[ 0 ]; 258 lx = WORDS[ 1 ]; 259 260 // Special cases `x`... 261 if ( lx === 0 ) { 262 if ( hx === 0 ) { 263 return xIsZero( x, y ); 264 } 265 if ( x === 1.0 ) { 266 return 1.0; 267 } 268 if ( 269 x === -1.0 && 270 isOdd( y ) 271 ) { 272 return -1.0; 273 } 274 if ( isInfinite( x ) ) { 275 if ( x === NINF ) { 276 // `pow( 1/x, -y )` 277 return pow( -0.0, -y ); 278 } 279 if ( y < 0.0 ) { 280 return 0.0; 281 } 282 return PINF; 283 } 284 } 285 if ( 286 x < 0.0 && 287 isInteger( y ) === false 288 ) { 289 // Signal NaN... 290 return (x-x)/(x-x); 291 } 292 ax = abs( x ); 293 294 // Remove the sign bits (i.e., get absolute values): 295 ahx = (hx & ABS_MASK)|0; // asm type annotation 296 ahy = (hy & ABS_MASK)|0; // asm type annotation 297 298 // Extract the sign bits: 299 sx = (hx >>> HIGH_NUM_NONSIGN_BITS)|0; // asm type annotation 300 sy = (hy >>> HIGH_NUM_NONSIGN_BITS)|0; // asm type annotation 301 302 // Determine the sign of the result... 303 if ( sx && isOdd( y ) ) { 304 sx = -1.0; 305 } else { 306 sx = 1.0; 307 } 308 // Case 1: `|y|` is huge... 309 310 // |y| > 2^31 311 if ( ahy > HIGH_BIASED_EXP_31 ) { 312 // `|y| > 2^64`, then must over- or underflow... 313 if ( ahy > HIGH_BIASED_EXP_64 ) { 314 return yIsHuge( x, y ); 315 } 316 // Over- or underflow if `x` is not close to unity... 317 318 if ( ahx < HIGH_MAX_NEAR_UNITY ) { 319 // y < 0 320 if ( sy === 1 ) { 321 // Signal overflow... 322 return sx * HUGE * HUGE; 323 } 324 // Signal underflow... 325 return sx * TINY * TINY; 326 } 327 if ( ahx > HIGH_BIASED_EXP_0 ) { 328 // y > 0 329 if ( sy === 0 ) { 330 // Signal overflow... 331 return sx * HUGE * HUGE; 332 } 333 // Signal underflow... 334 return sx * TINY * TINY; 335 } 336 // At this point, `|1-x|` is tiny (`<= 2^-20`). Suffice to compute `log(x)` by `x - x^2/2 + x^3/3 - x^4/4`. 337 t = logx( LOG_WORKSPACE, ax ); 338 } 339 // Case 2: `|y|` is not huge... 340 else { 341 t = log2ax( LOG_WORKSPACE, ax, ahx ); 342 } 343 // Split `y` into `y1 + y2` and compute `(y1+y2) * (t1+t2)`... 344 y1 = setLowWord( y, 0 ); 345 lp = ( (y-y1)*t[0] ) + ( y*t[1] ); 346 hp = y1 * t[0]; 347 z = lp + hp; 348 349 // Note: *can* be more performant to use `getHighWord` and `getLowWord` directly, but using `toWords` looks cleaner. 350 toWords( WORDS, z ); 351 j = uint32ToInt32( WORDS[0] ); 352 i = uint32ToInt32( WORDS[1] ); 353 354 // z >= 1024 355 if ( j >= HIGH_BIASED_EXP_10 ) { 356 // z > 1024 357 if ( ((j-HIGH_BIASED_EXP_10)|i) !== 0 ) { 358 // Signal overflow... 359 return sx * HUGE * HUGE; 360 } 361 if ( (lp+OVT) > (z-hp) ) { 362 // Signal overflow... 363 return sx * HUGE * HUGE; 364 } 365 } 366 // z <= -1075 367 else if ( (j&ABS_MASK) >= HIGH_1075 ) { 368 // z < -1075 369 if ( ((j-HIGH_NEG_1075)|i) !== 0 ) { 370 // signal underflow... 371 return sx * TINY * TINY; 372 } 373 if ( lp <= (z-hp) ) { 374 // signal underflow... 375 return sx * TINY * TINY; 376 } 377 } 378 // Compute `2^(hp+lp)`... 379 z = pow2( j, hp, lp ); 380 381 return sx * z; 382 } 383 384 385 // EXPORTS // 386 387 module.exports = pow;