UNPKG

@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
/** * @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. */ 'use strict'; // 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;