@stdlib/math-base-special-rempio2
Version:
Compute `x - nπ/2 = r`.
253 lines (226 loc) • 7.05 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.
*
*
* ## Notice
*
* 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/e_rem_pio2.c}. The implementation follows the original, but has been modified for JavaScript.
*
* ```text
* Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
*
* Developed at SunPro, a Sun Microsystems, Inc. business.
* Permission to use, copy, modify, and distribute this
* software is freely granted, provided that this notice
* is preserved.
*
* Optimized by Bruce D. Evans.
* ```
*/
'use strict';
// MODULES //
var ABS_MASK = require( '@stdlib/constants-float64-high-word-abs-mask' );
var EXPONENT_MASK = require( '@stdlib/constants-float64-high-word-exponent-mask' );
var SIGNIFICAND_MASK = require( '@stdlib/constants-float64-high-word-significand-mask' );
var getHighWord = require( '@stdlib/number-float64-base-get-high-word' );
var getLowWord = require( '@stdlib/number-float64-base-get-low-word' );
var fromWords = require( '@stdlib/number-float64-base-from-words' );
var rempio2Kernel = require( './kernel_rempio2.js' );
var rempio2Medium = require( './rempio2_medium.js' );
// VARIABLES //
var ZERO = 0.00000000000000000000e+00; // 0x00000000, 0x00000000
var TWO24 = 1.67772160000000000000e+07; // 0x41700000, 0x00000000
// 33 bits of π/2:
var PIO2_1 = 1.57079632673412561417e+00; // 0x3FF921FB, 0x54400000
// PIO2_1T = π/2 - PIO2_1:
var PIO2_1T = 6.07710050650619224932e-11; // 0x3DD0B461, 0x1A626331
var TWO_PIO2_1T = 2.0 * PIO2_1T;
var THREE_PIO2_1T = 3.0 * PIO2_1T;
var FOUR_PIO2_1T = 4.0 * PIO2_1T;
// High word significand for π and π/2: 0x921fb = 598523 => 00000000000010010010000111111011
var PI_HIGH_WORD_SIGNIFICAND = 0x921fb|0; // asm type annotation
// High word for π/4: 0x3fe921fb = 1072243195 => 00111111111010010010000111111011
var PIO4_HIGH_WORD = 0x3fe921fb|0; // asm type annotation
// High word for 3π/4: 0x4002d97c = 1073928572 => 01000000000000101101100101111100
var THREE_PIO4_HIGH_WORD = 0x4002d97c|0; // asm type annotation
// High word for 5π/4: 0x400f6a7a = 1074752122 => 01000000000011110110101001111010
var FIVE_PIO4_HIGH_WORD = 0x400f6a7a|0; // asm type annotation
// High word for 6π/4: 0x4012d97c = 1074977148 => 01000000000100101101100101111100
var THREE_PIO2_HIGH_WORD = 0x4012d97c|0; // asm type annotation
// High word for 7π/4: 0x4015fdbc = 1075183036 => 01000000000101011111110110111100
var SEVEN_PIO4_HIGH_WORD = 0x4015fdbc|0; // asm type annotation
// High word for 8π/4: 0x401921fb = 1075388923 => 01000000000110010010000111111011
var TWO_PI_HIGH_WORD = 0x401921fb|0; // asm type annotation
// High word for 9π/4: 0x401c463b = 1075594811 => 01000000000111000100011000111011
var NINE_PIO4_HIGH_WORD = 0x401c463b|0; // asm type annotation
// 2^20*π/2 = 1647099.3291652855 => 0100000100111001001000011111101101010100010001000010110100011000 => high word => 0x413921fb = 1094263291 => 01000001001110010010000111111011
var MEDIUM = 0x413921fb|0; // asm type annotation
// Arrays for storing temporary values:
var TX = [ 0.0, 0.0, 0.0 ];
var TY = [ 0.0, 0.0 ];
// MAIN //
/**
* Computes `x - nπ/2 = r`.
*
* ## Notes
*
* - Returns `n` and stores the remainder `r` as two numbers `y[0]` and `y[1]`, such that `y[0]+y[1] = r`.
*
* @param {number} x - input value
* @param {(Array|TypedArray|Object)} y - remainder elements
* @returns {integer} factor of `π/2`
*
* @example
* var y = [ 0.0, 0.0 ];
* var n = rempio2( 128.0, y );
* // returns 81
*
* var y1 = y[ 0 ];
* // returns ~0.765
*
* var y2 = y[ 1 ];
* // returns ~3.618e-17
*
* @example
* var y = [ 0.0, 0.0 ];
* var n = rempio2( NaN, y );
* // returns 0
*
* var y1 = y[ 0 ];
* // returns NaN
*
* var y2 = y[ 1 ];
* // returns NaN
*/
function rempio2( x, y ) {
var low;
var e0;
var hx;
var ix;
var nx;
var i;
var n;
var z;
hx = getHighWord( x );
ix = (hx & ABS_MASK)|0; // asm type annotation
// Case: |x| ~<= π/4 (no need for reduction)
if ( ix <= PIO4_HIGH_WORD ) {
y[ 0 ] = x;
y[ 1 ] = 0.0;
return 0;
}
// Case: |x| ~<= 5π/4
if ( ix <= FIVE_PIO4_HIGH_WORD ) {
// Case: |x| ~= π/2 or π
if ( (ix & SIGNIFICAND_MASK) === PI_HIGH_WORD_SIGNIFICAND ) {
// Cancellation => use medium case
return rempio2Medium( x, ix, y );
}
// Case: |x| ~<= 3π/4
if ( ix <= THREE_PIO4_HIGH_WORD ) {
if ( x > 0.0 ) {
z = x - PIO2_1;
y[ 0 ] = z - PIO2_1T;
y[ 1 ] = (z - y[0]) - PIO2_1T;
return 1;
}
z = x + PIO2_1;
y[ 0 ] = z + PIO2_1T;
y[ 1 ] = (z - y[0]) + PIO2_1T;
return -1;
}
if ( x > 0.0 ) {
z = x - ( 2.0*PIO2_1 );
y[ 0 ] = z - TWO_PIO2_1T;
y[ 1 ] = (z - y[0]) - TWO_PIO2_1T;
return 2;
}
z = x + ( 2.0*PIO2_1 );
y[ 0 ] = z + TWO_PIO2_1T;
y[ 1 ] = (z - y[0]) + TWO_PIO2_1T;
return -2;
}
// Case: |x| ~<= 9π/4
if ( ix <= NINE_PIO4_HIGH_WORD ) {
// Case: |x| ~<= 7π/4
if ( ix <= SEVEN_PIO4_HIGH_WORD ) {
// Case: |x| ~= 3π/2
if ( ix === THREE_PIO2_HIGH_WORD ) {
return rempio2Medium( x, ix, y );
}
if ( x > 0.0 ) {
z = x - ( 3.0*PIO2_1 );
y[ 0 ] = z - THREE_PIO2_1T;
y[ 1 ] = (z - y[0]) - THREE_PIO2_1T;
return 3;
}
z = x + ( 3.0*PIO2_1 );
y[ 0 ] = z + THREE_PIO2_1T;
y[ 1 ] = (z - y[0]) + THREE_PIO2_1T;
return -3;
}
// Case: |x| ~= 4π/2
if ( ix === TWO_PI_HIGH_WORD ) {
return rempio2Medium( x, ix, y );
}
if ( x > 0.0 ) {
z = x - ( 4.0*PIO2_1 );
y[ 0 ] = z - FOUR_PIO2_1T;
y[ 1 ] = (z - y[0]) - FOUR_PIO2_1T;
return 4;
}
z = x + ( 4.0*PIO2_1 );
y[ 0 ] = z + FOUR_PIO2_1T;
y[ 1 ] = (z - y[0]) + FOUR_PIO2_1T;
return -4;
}
// Case: |x| ~< 2^20*π/2 (medium size)
if ( ix < MEDIUM ) {
return rempio2Medium( x, ix, y );
}
// Case: x is NaN or infinity
if ( ix >= EXPONENT_MASK ) {
y[ 0 ] = NaN;
y[ 1 ] = NaN;
return 0.0;
}
// Set z = scalbn(|x|, ilogb(x)-23)...
low = getLowWord( x );
e0 = (ix >> 20) - 1046; // `e0 = ilogb(z) - 23` => unbiased exponent minus 23
z = fromWords( ix - ((e0 << 20)|0), low );
for ( i = 0; i < 2; i++ ) {
TX[ i ] = z|0;
z = (z - TX[i]) * TWO24;
}
TX[ 2 ] = z;
nx = 3;
while ( TX[ nx-1 ] === ZERO ) {
// Skip zero term...
nx -= 1;
}
n = rempio2Kernel( TX, TY, e0, nx, 1 );
if ( x < 0.0 ) {
y[ 0 ] = -TY[ 0 ];
y[ 1 ] = -TY[ 1 ];
return -n;
}
y[ 0 ] = TY[ 0 ];
y[ 1 ] = TY[ 1 ];
return n;
}
// EXPORTS //
module.exports = rempio2;