You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit 8251feb
Browse filesBrowse the repository at this point in the historyBrowse files
fix: avoid overflow and underflow for scales far from 1 in stats/base/dists/rayleigh/logpdf
For `sigma` outside about [1.5e-154, 1.3e154], `sigma^2` overflows or
underflows, and so does `x^2` for large `x`, so the log-PDF came out NaN
or a spurious -Infinity where it is finite: `logpdf( 1e200, 1e200 )` is
about -461.017 and returned NaN.
Keep the existing expression wherever it is safe — it is unchanged, bit for
bit, on every point of the existing fixtures — and otherwise evaluate via
the ratio `x/sigma`, taking `ln( x/sigma^2 )` directly while that ratio is
normal so the two logarithms don't cancel.
New `tiny_scale` and `huge_scale` fixtures cover both sides; develop fails
all 2,000 of their points.
@@ -46,7 +47,9 @@ var NINF = require( '@stdlib/constants/float64/ninf' );
46
47
* // returns ~-0.936
47
48
*/
48
49
functionfactory(sigma){
50
+
varnormal;
49
51
vars2i;
52
+
varlns;
50
53
vars2;
51
54
if(isnan(sigma)||sigma<0.0){
52
55
returnconstantFunction(NaN);
@@ -56,6 +59,8 @@ function factory( sigma ) {
56
59
}
57
60
s2=pow(sigma,2.0);
58
61
s2i=1.0/s2;
62
+
normal=(s2>=FLOAT64_SMALLEST_NORMAL&&s2<PINF);
63
+
lns=ln(sigma);
59
64
returnlogpdf;
60
65
61
66
/**
@@ -70,13 +75,32 @@ function factory( sigma ) {
70
75
* // returns <number>
71
76
*/
72
77
functionlogpdf(x){
78
+
varlq;
79
+
varx2;
80
+
varr;
81
+
varq;
73
82
if(isnan(x)){
74
83
returnNaN;
75
84
}
76
85
if(x<0.0||x===PINF){
77
86
returnNINF;
78
87
}
79
-
returnln(s2i*x)-(pow(x,2.0)/(2.0*s2));
88
+
x2=pow(x,2.0);
89
+
q=s2i*x;
90
+
if(normal&&x2<PINF&&q>=FLOAT64_SMALLEST_NORMAL){
91
+
returnln(q)-(x2/(2.0*s2));
92
+
}
93
+
// For `sigma` or `x` far from `1`, forming `sigma^2` or `x^2` overflows or underflows, so evaluate via the ratio `x/sigma` instead. `ln( x/sigma^2 )` is taken directly while that ratio is normal, as subtracting two large logarithms cancels when `x` is close to `sigma^2`...
@@ -63,7 +64,11 @@ var NINF = require( '@stdlib/constants/float64/ninf' );
63
64
*/
64
65
functionlogpdf(x,sigma){
65
66
vars2i;
67
+
varlq;
66
68
vars2;
69
+
varx2;
70
+
varr;
71
+
varq;
67
72
if(
68
73
isnan(x)||
69
74
isnan(sigma)||
@@ -79,7 +84,27 @@ function logpdf( x, sigma ) {
79
84
}
80
85
s2=pow(sigma,2.0);
81
86
s2i=1.0/s2;
82
-
returnln(s2i*x)-(pow(x,2.0)/(2.0*s2));
87
+
x2=pow(x,2.0);
88
+
q=s2i*x;
89
+
if(
90
+
s2>=FLOAT64_SMALLEST_NORMAL&&
91
+
s2<PINF&&
92
+
x2<PINF&&
93
+
q>=FLOAT64_SMALLEST_NORMAL
94
+
){
95
+
returnln(q)-(x2/(2.0*s2));
96
+
}
97
+
// For `sigma` or `x` far from `1`, forming `sigma^2` or `x^2` overflows or underflows, so evaluate via the ratio `x/sigma` instead. `ln( x/sigma^2 )` is taken directly while that ratio is normal, as subtracting two large logarithms cancels when `x` is close to `sigma^2`...
// For `sigma` or `x` far from `1`, forming `sigma^2` or `x^2` overflows or underflows, so evaluate via the ratio `x/sigma` instead. `ln( x/sigma^2 )` is taken directly while that ratio is normal, as subtracting two large logarithms cancels when `x` is close to `sigma^2`...
71
+
r=x / sigma;
72
+
q=r / sigma;
73
+
if ( q >= STDLIB_CONSTANT_FLOAT64_SMALLEST_NORMAL&&q<STDLIB_CONSTANT_FLOAT64_PINF ) {
74
+
lq=stdlib_base_ln( q );
75
+
} elseif ( r >= STDLIB_CONSTANT_FLOAT64_SMALLEST_NORMAL ) {
# The log PDF is evaluated in extended precision using the textbook formula `ln(x/sigma^2) - x^2/(2*sigma^2)`, as forming `sigma^2` or `x^2` in double precision overflows or underflows for scale parameters far from `1`...
42
+
z =setprecision( BigFloat, 2048 ) do
43
+
X =BigFloat.( x );
44
+
S =BigFloat.( sigma );
45
+
Float64.( log.( X ./ ( S .^2 ) ) .- ( ( X .^2 ) ./ ( 2.* ( S .^2 ) ) ) );
45
46
end
46
47
47
48
# Store data to be written to file as a collection:
0 commit comments