time-to-botec

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

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;