@stdlib/math-base-special-ldexp
Version:
Multiply a double-precision floating-point number by an integer power of two.
151 lines (127 loc) • 3.97 kB
JavaScript
/**
* @license Apache-2.0
*
* Copyright (c) 2018 The Stdlib Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
;
// NOTES //
/*
* => ldexp: load exponent (see [The Open Group]{@link http://pubs.opengroup.org/onlinepubs/9699919799/functions/ldexp.html} and [cppreference]{@link http://en.cppreference.com/w/c/numeric/math/ldexp}).
*/
// MODULES //
var PINF = require( '@stdlib/constants-float64-pinf' );
var NINF = require( '@stdlib/constants-float64-ninf' );
var BIAS = require( '@stdlib/constants-float64-exponent-bias' );
var MAX_EXPONENT = require( '@stdlib/constants-float64-max-base2-exponent' );
var MAX_SUBNORMAL_EXPONENT = require( '@stdlib/constants-float64-max-base2-exponent-subnormal' );
var MIN_SUBNORMAL_EXPONENT = require( '@stdlib/constants-float64-min-base2-exponent-subnormal' );
var isnan = require( '@stdlib/math-base-assert-is-nan' );
var isInfinite = require( '@stdlib/math-base-assert-is-infinite' );
var copysign = require( '@stdlib/math-base-special-copysign' );
var normalize = require( '@stdlib/number-float64-base-normalize' ).assign;
var floatExp = require( '@stdlib/number-float64-base-exponent' );
var toWords = require( '@stdlib/number-float64-base-to-words' );
var fromWords = require( '@stdlib/number-float64-base-from-words' );
// VARIABLES //
// 1/(1<<52) = 1/(2**52) = 1/4503599627370496
var TWO52_INV = 2.220446049250313e-16;
// Exponent all 0s: 1 00000000000 11111111111111111111 => 2148532223
var CLEAR_EXP_MASK = 0x800fffff>>>0; // asm type annotation
// Normalization workspace:
var FRAC = [ 0.0, 0.0 ];
// High/low words workspace:
var WORDS = [ 0, 0 ];
// MAIN //
/**
* Multiplies a double-precision floating-point number by an integer power of two.
*
* @param {number} frac - fraction
* @param {integer} exp - exponent
* @returns {number} double-precision floating-point number
*
* @example
* var x = ldexp( 0.5, 3 ); // => 0.5 * 2^3 = 0.5 * 8
* // returns 4.0
*
* @example
* var x = ldexp( 4.0, -2 ); // => 4 * 2^(-2) = 4 * (1/4)
* // returns 1.0
*
* @example
* var x = ldexp( 0.0, 20 );
* // returns 0.0
*
* @example
* var x = ldexp( -0.0, 39 );
* // returns -0.0
*
* @example
* var x = ldexp( NaN, -101 );
* // returns NaN
*
* @example
* var x = ldexp( Infinity, 11 );
* // returns Infinity
*
* @example
* var x = ldexp( -Infinity, -118 );
* // returns -Infinity
*/
function ldexp( frac, exp ) {
var high;
var m;
if (
exp === 0 ||
frac === 0.0 || // handles +-0
isnan( frac ) ||
isInfinite( frac )
) {
return frac;
}
// Normalize the input fraction:
normalize( frac, FRAC, 1, 0 );
frac = FRAC[ 0 ];
exp += FRAC[ 1 ];
// Extract the exponent from `frac` and add it to `exp`:
exp += floatExp( frac );
// Check for underflow/overflow...
if ( exp < MIN_SUBNORMAL_EXPONENT ) {
return copysign( 0.0, frac );
}
if ( exp > MAX_EXPONENT ) {
if ( frac < 0.0 ) {
return NINF;
}
return PINF;
}
// Check for a subnormal and scale accordingly to retain precision...
if ( exp <= MAX_SUBNORMAL_EXPONENT ) {
exp += 52;
m = TWO52_INV;
} else {
m = 1.0;
}
// Split the fraction into higher and lower order words:
toWords.assign( frac, WORDS, 1, 0 );
high = WORDS[ 0 ];
// Clear the exponent bits within the higher order word:
high &= CLEAR_EXP_MASK;
// Set the exponent bits to the new exponent:
high |= ((exp+BIAS) << 20);
// Create a new floating-point number:
return m * fromWords( high, WORDS[ 1 ] );
}
// EXPORTS //
module.exports = ldexp;