Skip to content

Commit 65c3b27

Browse files
gunjjoshikgryte
andauthored
refactor: update math/base/special/log10 implementation
PR-URL: #2176 Co-authored-by: Athan Reines <[email protected]> Reviewed-by: Athan Reines <[email protected]> Signed-off-by: GUNJ JOSHI <[email protected]>
1 parent be5ece8 commit 65c3b27

File tree

8 files changed

+70
-353
lines changed

8 files changed

+70
-353
lines changed

Diff for: lib/node_modules/@stdlib/math/base/special/log10/lib/klog.js

-102
This file was deleted.

Diff for: lib/node_modules/@stdlib/math/base/special/log10/lib/main.js

+63-25
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
*
1919
* ## Notice
2020
*
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/e_log10.c}. The implementation follows the original, but has been modified for JavaScript.
21+
* The following copyright and license were part of the original implementation available as part of [FreeBSD]{@link https://svnweb.freebsd.org/base/release/12.2.0/lib/msun/src/e_log10.c}. The implementation follows the original, but has been modified for JavaScript.
2222
*
2323
* ```text
2424
* Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
@@ -37,10 +37,13 @@
3737
var getHighWord = require( '@stdlib/number/float64/base/get-high-word' );
3838
var setHighWord = require( '@stdlib/number/float64/base/set-high-word' );
3939
var setLowWord = require( '@stdlib/number/float64/base/set-low-word' );
40+
var toWords = require( '@stdlib/number/float64/base/to-words' );
41+
var ABS_MASK = require( '@stdlib/constants/float64/high-word-abs-mask' );
42+
var HIGH_SIGNIFICAND_MASK = require( '@stdlib/constants/float64/high-word-significand-mask' );
4043
var isnan = require( '@stdlib/math/base/assert/is-nan' );
4144
var BIAS = require( '@stdlib/constants/float64/exponent-bias' );
4245
var NINF = require( '@stdlib/constants/float64/ninf' );
43-
var klog = require( './klog.js' );
46+
var kernelLog1p = require( '@stdlib/math/base/special/kernel-log1p' );
4447

4548

4649
// VARIABLES //
@@ -51,9 +54,6 @@ var IVLN10LO = 2.50829467116452752298e-11; // 0x3dbb9438, 0xca9aadd5
5154
var LOG10_2HI = 3.01029995663611771306e-01; // 0x3FD34413, 0x509F6000
5255
var LOG10_2LO = 3.69423907715893078616e-13; // 0x3D59FEF3, 0x11F12B36
5356

54-
// 0x000fffff = 1048575 => 0 00000000000 11111111111111111111
55-
var HIGH_SIGNIFICAND_MASK = 0x000fffff|0; // asm type annotation
56-
5757
// 0x7ff00000 = 2146435072 => 0 11111111111 00000000000000000000 => biased exponent: 2047 = 1023+1023 => 2^1023
5858
var HIGH_MAX_NORMAL_EXP = 0x7ff00000|0; // asm type annotation
5959

@@ -63,6 +63,9 @@ var HIGH_MIN_NORMAL_EXP = 0x00100000|0; // asm type annotation
6363
// 0x3ff00000 = 1072693248 => 0 01111111111 00000000000000000000 => biased exponent: 1023 = 0+1023 => 2^0 = 1
6464
var HIGH_BIASED_EXP_0 = 0x3ff00000|0; // asm type annotation
6565

66+
// High/low words workspace:
67+
var WORDS = [ 0|0, 0|0 ];
68+
6669

6770
// MAIN //
6871

@@ -97,49 +100,84 @@ var HIGH_BIASED_EXP_0 = 0x3ff00000|0; // asm type annotation
97100
* // returns NaN
98101
*/
99102
function log10( x ) {
103+
var valHi;
104+
var valLo;
105+
var hfsq;
100106
var hi;
101-
var hx;
102107
var lo;
108+
var hx;
109+
var lx;
110+
var y2;
103111
var f;
112+
var R;
113+
var w;
114+
var y;
104115
var i;
105116
var k;
106-
var y;
107-
var z;
108117

109118
if ( isnan( x ) || x < 0.0 ) {
110119
return NaN;
111120
}
112-
if ( x === 0.0 ) {
113-
return NINF;
114-
}
115-
hx = getHighWord( x );
121+
toWords.assign( x, WORDS, 1, 0 );
122+
hx = WORDS[ 0 ];
123+
lx = WORDS[ 1 ];
116124
k = 0|0; // asm type annotation
117125

118-
// Case: 0 < x < 2**-1022
119126
if ( hx < HIGH_MIN_NORMAL_EXP ) {
120-
// Subnormal number, scale up `x`...
127+
// Case: x < 2**-1022
128+
if ( ( ( hx & ABS_MASK ) | lx ) === 0 ) {
129+
return NINF;
130+
}
121131
k -= 54|0; // asm type annotation
132+
133+
// Subnormal number, scale up x:
122134
x *= TWO54;
123135
hx = getHighWord( x );
124136
}
125137
if ( hx >= HIGH_MAX_NORMAL_EXP ) {
126138
return x + x;
127139
}
128-
k += ((hx>>20) - BIAS)|0; // asm type annotation
140+
// Case: log(1) = +0
141+
if ( hx === HIGH_BIASED_EXP_0 && lx === 0 ) {
142+
return 0.0;
143+
}
144+
k += ( ( hx >> 20 ) - BIAS )|0; // asm type annotation
129145
hx &= HIGH_SIGNIFICAND_MASK;
130-
i = ( (hx+0x95f64)&0x100000 )|0; // asm type annotation
146+
i = ( ( hx + 0x95f64 ) & HIGH_MIN_NORMAL_EXP )|0; // asm type annotation
131147

132148
// Normalize `x` or `x/2`...
133-
x = setHighWord( x, hx|(i^HIGH_BIASED_EXP_0) );
134-
k += (i>>20)|0; // asm type annotation
149+
x = setHighWord( x, hx | ( i ^ HIGH_BIASED_EXP_0 ) );
150+
k += ( i >> 20 )|0; // asm type annotation
135151
y = k;
136-
f = klog( x );
137-
x -= 1;
138-
hi = setLowWord( x, 0.0 );
139-
lo = x - hi;
140-
z = (y*LOG10_2LO) + ( (x+f)*IVLN10LO );
141-
z += ( (lo+f)*IVLN10HI ) + ( hi*IVLN10HI );
142-
return z + ( y*LOG10_2HI );
152+
f = x - 1.0;
153+
hfsq = 0.5 * f * f;
154+
R = kernelLog1p( f );
155+
156+
/*
157+
* Notes:
158+
*
159+
* - `f-hfsq` must (for args near `1`) be evaluated in extra precision to avoid a large cancellation when `x` is near `sqrt(2)` or `1/sqrt(2)`.This is fairly efficient since `f-hfsq` only depends on `f`, so can be evaluated in parallel with `R`. Not combining `hfsq` with `R` also keeps `R` small (though not as small as a true `lo` term would be), so that extra precision is not needed for terms involving `R`.
160+
* - When implemented in C, compiler bugs involving extra precision used to break Dekker's theorem for spitting `f-hfsq` as `hi+lo`. These problems are now automatically avoided as a side effect of the optimization of combining the Dekker splitting step with the clear-low-bits step.
161+
* - This implementation uses Dekker's theorem to normalize `y+val_hi`, so, when implemented in C, compiler bugs may reappear in some configurations.
162+
* - The multi-precision calculations for the multiplications are routine.
163+
*/
164+
hi = f - hfsq;
165+
hi = setLowWord( hi, 0 );
166+
lo = ( f - hi ) - hfsq + R;
167+
valHi = hi * IVLN10HI;
168+
y2 = y * LOG10_2HI;
169+
valLo = ( y * LOG10_2LO ) + ( ( lo + hi ) * IVLN10LO ) + ( lo * IVLN10HI );
170+
171+
/*
172+
* Note:
173+
*
174+
* - Extra precision in for adding `y*log10_2hi` is not strictly needed since there is no very large cancellation near `x = sqrt(2)` or `x = 1/sqrt(2)`, but we do it anyway since it costs little on CPUs with some parallelism and it reduces the error for many args.
175+
*/
176+
w = y2 + valHi;
177+
valLo += ( y2 - w ) + valHi;
178+
valHi = w;
179+
180+
return valLo + valHi;
143181
}
144182

145183

Diff for: lib/node_modules/@stdlib/math/base/special/log10/lib/polyval_p.js

-47
This file was deleted.

Diff for: lib/node_modules/@stdlib/math/base/special/log10/lib/polyval_q.js

-47
This file was deleted.

Diff for: lib/node_modules/@stdlib/math/base/special/log10/package.json

-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
"doc": "./docs",
2020
"example": "./examples",
2121
"lib": "./lib",
22-
"scripts": "./scripts",
2322
"test": "./test"
2423
},
2524
"types": "./docs/types",

0 commit comments

Comments
 (0)