Skip to content

Commit d3215eb

Browse files
authored
refactor: update math/base/special/log10 to follow FreeBSD version 12.2.0
PR-URL: #2215 Reviewed-by: Philipp Burckhardt <[email protected]>
1 parent 1414d1a commit d3215eb

File tree

4 files changed

+79
-132
lines changed

4 files changed

+79
-132
lines changed

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

+2-2
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ function log10( x ) {
156156
/*
157157
* Notes:
158158
*
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`.
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`.
160160
* - 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.
161161
* - This implementation uses Dekker's theorem to normalize `y+val_hi`, so, when implemented in C, compiler bugs may reappear in some configurations.
162162
* - The multi-precision calculations for the multiplications are routine.
@@ -171,7 +171,7 @@ function log10( x ) {
171171
/*
172172
* Note:
173173
*
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.
174+
* - Extra precision 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.
175175
*/
176176
w = y2 + valHi;
177177
valLo += ( y2 - w ) + valHi;

lib/node_modules/@stdlib/math/base/special/log10/manifest.json

+9-3
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,9 @@
4444
"@stdlib/number/float64/base/set-high-word",
4545
"@stdlib/constants/float64/high-word-abs-mask",
4646
"@stdlib/constants/float64/high-word-significand-mask",
47-
"@stdlib/number/float64/base/set-low-word"
47+
"@stdlib/number/float64/base/set-low-word",
48+
"@stdlib/math/base/special/kernel-log1p",
49+
"@stdlib/math/base/assert/is-nan"
4850
]
4951
},
5052
{
@@ -65,7 +67,9 @@
6567
"@stdlib/number/float64/base/set-high-word",
6668
"@stdlib/constants/float64/high-word-abs-mask",
6769
"@stdlib/constants/float64/high-word-significand-mask",
68-
"@stdlib/number/float64/base/set-low-word"
70+
"@stdlib/number/float64/base/set-low-word",
71+
"@stdlib/math/base/special/kernel-log1p",
72+
"@stdlib/math/base/assert/is-nan"
6973
]
7074
},
7175
{
@@ -86,7 +90,9 @@
8690
"@stdlib/number/float64/base/set-high-word",
8791
"@stdlib/constants/float64/high-word-abs-mask",
8892
"@stdlib/constants/float64/high-word-significand-mask",
89-
"@stdlib/number/float64/base/set-low-word"
93+
"@stdlib/number/float64/base/set-low-word",
94+
"@stdlib/math/base/special/kernel-log1p",
95+
"@stdlib/math/base/assert/is-nan"
9096
]
9197
}
9298
]

lib/node_modules/@stdlib/math/base/special/log10/src/main.c

+61-127
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.
@@ -39,6 +39,9 @@
3939
#include "stdlib/constants/float64/high_word_significand_mask.h"
4040
#include "stdlib/constants/float64/exponent_bias.h"
4141
#include "stdlib/constants/float64/ninf.h"
42+
#include "stdlib/math/base/assert/is_nan.h"
43+
#include "stdlib/math/base/special/kernel_log1p.h"
44+
#include <stdint.h>
4245

4346
static const double TWO54 = 1.80143985094819840000e+16; // 0x43500000, 0x00000000
4447
static const double IVLN10HI = 4.34294481878168880939e-01; // 0x3fdbcb7b, 0x15200000
@@ -55,100 +58,6 @@ static const int32_t HIGH_MIN_NORMAL_EXP = 0x00100000;
5558
// 0x3ff00000 = 1072693248 => 0 01111111111 00000000000000000000 => biased exponent: 1023 = 0+1023 => 2^0 = 1
5659
static const int32_t HIGH_BIASED_EXP_0 = 0x3ff00000;
5760

58-
// 1/3
59-
static const double ONE_THIRD = 0.33333333333333333;
60-
61-
/* Begin auto-generated functions. The following functions are auto-generated. Do not edit directly. */
62-
63-
// BEGIN: polyval_p
64-
65-
/**
66-
* Evaluates a polynomial.
67-
*
68-
* ## Notes
69-
*
70-
* - The implementation uses [Horner's rule][horners-method] for efficient computation.
71-
*
72-
* [horners-method]: https://en.wikipedia.org/wiki/Horner%27s_method
73-
*
74-
* @param x value at which to evaluate the polynomial
75-
* @return evaluated polynomial
76-
*/
77-
static double polyval_p( const double x ) {
78-
return 0.3999999999940942 + (x * (0.22222198432149784 + (x * 0.15313837699209373)));
79-
}
80-
81-
// END: polyval_p
82-
83-
// BEGIN: polyval_q
84-
85-
/**
86-
* Evaluates a polynomial.
87-
*
88-
* ## Notes
89-
*
90-
* - The implementation uses [Horner's rule][horners-method] for efficient computation.
91-
*
92-
* [horners-method]: https://en.wikipedia.org/wiki/Horner%27s_method
93-
*
94-
* @param x value at which to evaluate the polynomial
95-
* @return evaluated polynomial
96-
*/
97-
static double polyval_q( const double x ) {
98-
return 0.6666666666666735 + (x * (0.2857142874366239 + (x * (0.1818357216161805 + (x * 0.14798198605116586)))));
99-
}
100-
101-
// END: polyval_q
102-
103-
/* End auto-generated functions. */
104-
105-
/**
106-
* Return `log(x) - (x-1)` for `x` in `~[sqrt(2)/2, sqrt(2)]`.
107-
*
108-
* @param {number} x - input value
109-
* @returns {number} function value
110-
*/
111-
static double klog( const double x ) {
112-
double hfsq;
113-
uint32_t hx;
114-
int32_t ihx;
115-
int32_t i;
116-
int32_t j;
117-
double t1;
118-
double t2;
119-
double f;
120-
double s;
121-
double z;
122-
double R;
123-
double w;
124-
125-
stdlib_base_float64_get_high_word( x, &hx );
126-
ihx = (int32_t)hx;
127-
f = x - 1.0;
128-
if ( ( STDLIB_CONSTANT_FLOAT64_HIGH_WORD_SIGNIFICAND_MASK & ( 2 + ihx ) ) < 3 ) {
129-
// Case: -2**-20 <= f < 2**-20
130-
if ( f == 0.0 ) {
131-
return 0.0;
132-
}
133-
return f * f * ( ( ONE_THIRD * f ) - 0.5 );
134-
}
135-
s = f / ( 2.0 + f );
136-
z = s * s;
137-
ihx &= STDLIB_CONSTANT_FLOAT64_HIGH_WORD_SIGNIFICAND_MASK;
138-
i = (ihx - 0x6147a);
139-
w = z * z;
140-
j = ( 0x6b851 - ihx );
141-
t1 = w * polyval_p( w );
142-
t2 = z * polyval_q( w );
143-
i |= j;
144-
R = t2 + t1;
145-
if ( i > 0 ) {
146-
hfsq = 0.5 * f * f;
147-
return ( s * ( hfsq + R ) ) - hfsq;
148-
}
149-
return s * ( R - f );
150-
}
151-
15261
/**
15362
* Evaluates the common logarithm (base ten).
15463
*
@@ -160,56 +69,81 @@ static double klog( const double x ) {
16069
* // returns ~0.602
16170
*/
16271
double stdlib_base_log10( const double x ) {
163-
int32_t ihx;
164-
int32_t ilx;
72+
double valLo;
73+
double valHi;
16574
uint32_t hx;
16675
uint32_t lx;
76+
double hfsq;
77+
double hi;
16778
int32_t i;
16879
int32_t k;
169-
double xc;
170-
double hi;
17180
double lo;
81+
double xc;
82+
double y2;
17283
double f;
84+
double R;
85+
double w;
17386
double y;
174-
double z;
17587

88+
if ( stdlib_base_is_nan( x ) || x < 0.0 ) {
89+
return 0.0 / 0.0; // NaN
90+
}
17691
xc = x;
177-
stdlib_base_float64_to_words( x, &hx, &lx );
178-
ihx = (int32_t)hx;
179-
ilx = (int32_t)lx;
92+
stdlib_base_float64_to_words( xc, &hx, &lx );
18093
k = 0;
181-
182-
// Case: 0 < x < 2**-1022
183-
if ( ihx < HIGH_MIN_NORMAL_EXP ) {
184-
if ( ( ( ihx & STDLIB_CONSTANT_FLOAT64_HIGH_WORD_ABS_MASK ) | ilx ) == 0 ) {
94+
if ( hx < HIGH_MIN_NORMAL_EXP ) {
95+
// Case: x < 2**-1022
96+
if ( ( ( hx & STDLIB_CONSTANT_FLOAT64_HIGH_WORD_ABS_MASK ) | lx ) == 0 ) {
18597
return STDLIB_CONSTANT_FLOAT64_NINF;
18698
}
187-
if ( ihx < 0 ) {
188-
return 0.0 / 0.0; // NaN
189-
}
190-
// Subnormal number, scale up `x`...
19199
k -= 54;
100+
101+
// Subnormal number, scale up x:
192102
xc *= TWO54;
193103
stdlib_base_float64_get_high_word( xc, &hx );
194-
ihx = (int32_t)hx;
195104
}
196-
if ( ihx >= HIGH_MAX_NORMAL_EXP ) {
197-
return x + x;
105+
if ( hx >= HIGH_MAX_NORMAL_EXP ) {
106+
return xc + xc;
107+
}
108+
// Case: log(1) = +0
109+
if ( hx == HIGH_BIASED_EXP_0 && lx == 0 ) {
110+
return 0;
198111
}
199-
k += ( ( ihx>>20 ) - STDLIB_CONSTANT_FLOAT64_EXPONENT_BIAS );
200-
ihx &= STDLIB_CONSTANT_FLOAT64_HIGH_WORD_SIGNIFICAND_MASK;
201-
i = ( ihx + 0x95f64 ) & 0x100000;
112+
k += ( ( hx >> 20 ) - STDLIB_CONSTANT_FLOAT64_EXPONENT_BIAS );
113+
hx &= STDLIB_CONSTANT_FLOAT64_HIGH_WORD_SIGNIFICAND_MASK;
114+
i = ( hx + 0x95f64 ) & HIGH_MIN_NORMAL_EXP;
202115

203-
// Normalize `x` or `x/2`...
204-
stdlib_base_float64_set_high_word( ihx | ( i ^ HIGH_BIASED_EXP_0 ), &xc );
205-
k += i >> 20;
116+
// Normalize x or x/2...
117+
stdlib_base_float64_set_high_word( hx | ( i ^ HIGH_BIASED_EXP_0 ), &xc );
118+
k += ( i >> 20 );
206119
y = (double)k;
207-
f = klog( xc );
208-
xc -= 1.0;
209-
hi = xc;
120+
f = xc - 1.0;
121+
hfsq = 0.5 * f * f;
122+
R = stdlib_base_kernel_log1p( f );
123+
124+
/*
125+
* Notes:
126+
*
127+
* - `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`.
128+
* - 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.
129+
* - This implementation uses Dekker's theorem to normalize `y+val_hi`, so, when implemented in C, compiler bugs may reappear in some configurations.
130+
* - The multi-precision calculations for the multiplications are routine.
131+
*/
132+
hi = f - hfsq;
210133
stdlib_base_float64_set_low_word( 0, &hi );
211-
lo = xc - hi;
212-
z = ( y * LOG10_2LO ) + ( ( xc + f ) * IVLN10LO );
213-
z += ( ( lo + f ) * IVLN10HI ) + ( hi * IVLN10HI );
214-
return z + ( y * LOG10_2HI );
134+
lo = ( f - hi ) - hfsq + R;
135+
valHi = hi * IVLN10HI;
136+
y2 = y * LOG10_2HI;
137+
valLo = ( y * LOG10_2LO ) + ( ( lo + hi ) * IVLN10LO ) + ( lo * IVLN10HI );
138+
139+
/*
140+
* Note:
141+
*
142+
* - Extra precision 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.
143+
*/
144+
w = y2 + valHi;
145+
valLo += ( y2 - w ) + valHi;
146+
valHi = w;
147+
148+
return valLo + valHi;
215149
}

lib/node_modules/@stdlib/math/base/special/log10/test/test.native.js

+7
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ var NINF = require( '@stdlib/constants/float64/ninf' );
2828
var tryRequire = require( '@stdlib/utils/try-require' );
2929
var EPS = require( '@stdlib/constants/float64/eps' );
3030
var abs = require( '@stdlib/math/base/special/abs' );
31+
var isPositiveZero = require( '@stdlib/math/base/assert/is-positive-zero' );
3132

3233

3334
// FIXTURES //
@@ -233,3 +234,9 @@ tape( 'the function returns `NaN` if provided a negative number', opts, function
233234
t.equal( isnan( v ), true, 'returns NaN' );
234235
t.end();
235236
});
237+
238+
tape( 'the function returns positive zero if provided `1.0`', opts, function test( t ) {
239+
var v = log10( 1.0 );
240+
t.equal( isPositiveZero( v ), true, 'returns +0' );
241+
t.end();
242+
});

0 commit comments

Comments
 (0)