From ff86d3bcf66908ce5eb56f37f9f32ad9588200e2 Mon Sep 17 00:00:00 2001 From: Abhist17 Date: Fri, 2 Oct 2026 04:49:30 +0530 Subject: [PATCH 1/2] fix: use `log1p` for small `p` in `stats/base/dists/planck/quantile` `1 - p` rounds away the low-order bits of `p`. For `p < 2^-53` the function returned `-1`, outside the support, and for small `lambda` it was off by more than 10%. --- .../base/dists/planck/quantile/lib/factory.js | 4 ++-- .../base/dists/planck/quantile/lib/main.js | 4 ++-- .../base/dists/planck/quantile/manifest.json | 6 +++--- .../base/dists/planck/quantile/src/main.c | 4 ++-- .../planck/quantile/test/test.factory.js | 21 +++++++++++++++++++ .../dists/planck/quantile/test/test.native.js | 17 +++++++++++++++ .../planck/quantile/test/test.quantile.js | 17 +++++++++++++++ 7 files changed, 64 insertions(+), 9 deletions(-) diff --git a/lib/node_modules/@stdlib/stats/base/dists/planck/quantile/lib/factory.js b/lib/node_modules/@stdlib/stats/base/dists/planck/quantile/lib/factory.js index 8beaa7405647..22a98a30489a 100644 --- a/lib/node_modules/@stdlib/stats/base/dists/planck/quantile/lib/factory.js +++ b/lib/node_modules/@stdlib/stats/base/dists/planck/quantile/lib/factory.js @@ -23,7 +23,7 @@ var constantFunction = require( '@stdlib/utils/constant-function' ); var isnan = require( '@stdlib/math/base/assert/is-nan' ); var ceil = require( '@stdlib/math/base/special/ceil' ); -var ln = require( '@stdlib/math/base/special/ln' ); +var log1p = require( '@stdlib/math/base/special/log1p' ); var PINF = require( '@stdlib/constants/float64/pinf' ); @@ -70,7 +70,7 @@ function factory( lambda ) { if ( p === 1.0 ) { return PINF; } - return ceil( -ln( 1.0-p ) / lambda ) - 1.0; + return ceil( -log1p( -p ) / lambda ) - 1.0; } } diff --git a/lib/node_modules/@stdlib/stats/base/dists/planck/quantile/lib/main.js b/lib/node_modules/@stdlib/stats/base/dists/planck/quantile/lib/main.js index 90639967c5d1..f887ff548863 100644 --- a/lib/node_modules/@stdlib/stats/base/dists/planck/quantile/lib/main.js +++ b/lib/node_modules/@stdlib/stats/base/dists/planck/quantile/lib/main.js @@ -22,7 +22,7 @@ var isnan = require( '@stdlib/math/base/assert/is-nan' ); var ceil = require( '@stdlib/math/base/special/ceil' ); -var ln = require( '@stdlib/math/base/special/ln' ); +var log1p = require( '@stdlib/math/base/special/log1p' ); var PINF = require( '@stdlib/constants/float64/pinf' ); @@ -74,7 +74,7 @@ function quantile( p, lambda ) { if ( p === 1.0 ) { return PINF; } - return ceil( -ln( 1.0-p ) / lambda ) - 1.0; + return ceil( -log1p( -p ) / lambda ) - 1.0; } diff --git a/lib/node_modules/@stdlib/stats/base/dists/planck/quantile/manifest.json b/lib/node_modules/@stdlib/stats/base/dists/planck/quantile/manifest.json index 36f6fecd0dfc..9cd530ceb45e 100644 --- a/lib/node_modules/@stdlib/stats/base/dists/planck/quantile/manifest.json +++ b/lib/node_modules/@stdlib/stats/base/dists/planck/quantile/manifest.json @@ -41,7 +41,7 @@ "@stdlib/math/base/napi/binary", "@stdlib/math/base/assert/is-nan", "@stdlib/math/base/special/ceil", - "@stdlib/math/base/special/ln", + "@stdlib/math/base/special/log1p", "@stdlib/constants/float64/pinf" ] }, @@ -59,7 +59,7 @@ "dependencies": [ "@stdlib/math/base/assert/is-nan", "@stdlib/math/base/special/ceil", - "@stdlib/math/base/special/ln", + "@stdlib/math/base/special/log1p", "@stdlib/constants/float64/pinf" ] }, @@ -77,7 +77,7 @@ "dependencies": [ "@stdlib/math/base/assert/is-nan", "@stdlib/math/base/special/ceil", - "@stdlib/math/base/special/ln", + "@stdlib/math/base/special/log1p", "@stdlib/constants/float64/pinf" ] } diff --git a/lib/node_modules/@stdlib/stats/base/dists/planck/quantile/src/main.c b/lib/node_modules/@stdlib/stats/base/dists/planck/quantile/src/main.c index 91905928207d..1d65f7275d1a 100644 --- a/lib/node_modules/@stdlib/stats/base/dists/planck/quantile/src/main.c +++ b/lib/node_modules/@stdlib/stats/base/dists/planck/quantile/src/main.c @@ -19,7 +19,7 @@ #include "stdlib/stats/base/dists/planck/quantile.h" #include "stdlib/math/base/assert/is_nan.h" #include "stdlib/math/base/special/ceil.h" -#include "stdlib/math/base/special/ln.h" +#include "stdlib/math/base/special/log1p.h" #include "stdlib/constants/float64/pinf.h" /** @@ -40,5 +40,5 @@ double stdlib_base_dists_planck_quantile( const double p, const double lambda ) if ( p == 1.0 ) { return STDLIB_CONSTANT_FLOAT64_PINF; } - return stdlib_base_ceil( -stdlib_base_ln( 1.0 - p ) / lambda ) - 1.0; + return stdlib_base_ceil( -stdlib_base_log1p( -p ) / lambda ) - 1.0; } diff --git a/lib/node_modules/@stdlib/stats/base/dists/planck/quantile/test/test.factory.js b/lib/node_modules/@stdlib/stats/base/dists/planck/quantile/test/test.factory.js index 44427a1e4fc6..4b9789a73e98 100644 --- a/lib/node_modules/@stdlib/stats/base/dists/planck/quantile/test/test.factory.js +++ b/lib/node_modules/@stdlib/stats/base/dists/planck/quantile/test/test.factory.js @@ -146,3 +146,24 @@ tape( 'the returned function evaluates the quantile for `p` given large paramete } t.end(); }); + +tape( 'the created function evaluates the quantile for small `p`', function test( t ) { + var quantile; + var y; + + quantile = factory( 1.0 ); + y = quantile( 1.0e-20 ); + t.strictEqual( y, 0.0, 'returns expected value' ); + + y = quantile( 1.0e-300 ); + t.strictEqual( y, 0.0, 'returns expected value' ); + + quantile = factory( 1.0e-17 ); + y = quantile( 1.0e-15 ); + t.strictEqual( y, 100.0, 'returns expected value' ); + + quantile = factory( 1.0e-14 ); + y = quantile( 1.0e-12 ); + t.strictEqual( y, 100.0, 'returns expected value' ); + t.end(); +}); diff --git a/lib/node_modules/@stdlib/stats/base/dists/planck/quantile/test/test.native.js b/lib/node_modules/@stdlib/stats/base/dists/planck/quantile/test/test.native.js index 97e97b213345..4d4579c949ef 100644 --- a/lib/node_modules/@stdlib/stats/base/dists/planck/quantile/test/test.native.js +++ b/lib/node_modules/@stdlib/stats/base/dists/planck/quantile/test/test.native.js @@ -118,3 +118,20 @@ tape( 'the function evaluates the quantile for `p` given large parameter `lambda } t.end(); }); + +tape( 'the function evaluates the quantile for small `p`', opts, function test( t ) { + var y; + + y = quantile( 1.0e-20, 1.0 ); + t.strictEqual( y, 0.0, 'returns expected value' ); + + y = quantile( 1.0e-300, 1.0 ); + t.strictEqual( y, 0.0, 'returns expected value' ); + + y = quantile( 1.0e-15, 1.0e-17 ); + t.strictEqual( y, 100.0, 'returns expected value' ); + + y = quantile( 1.0e-12, 1.0e-14 ); + t.strictEqual( y, 100.0, 'returns expected value' ); + t.end(); +}); diff --git a/lib/node_modules/@stdlib/stats/base/dists/planck/quantile/test/test.quantile.js b/lib/node_modules/@stdlib/stats/base/dists/planck/quantile/test/test.quantile.js index f7a0122742d3..1d8cb47c3085 100644 --- a/lib/node_modules/@stdlib/stats/base/dists/planck/quantile/test/test.quantile.js +++ b/lib/node_modules/@stdlib/stats/base/dists/planck/quantile/test/test.quantile.js @@ -109,3 +109,20 @@ tape( 'the function evaluates the quantile for `p` given large parameter `lambda } t.end(); }); + +tape( 'the function evaluates the quantile for small `p`', function test( t ) { + var y; + + y = quantile( 1.0e-20, 1.0 ); + t.strictEqual( y, 0.0, 'returns expected value' ); + + y = quantile( 1.0e-300, 1.0 ); + t.strictEqual( y, 0.0, 'returns expected value' ); + + y = quantile( 1.0e-15, 1.0e-17 ); + t.strictEqual( y, 100.0, 'returns expected value' ); + + y = quantile( 1.0e-12, 1.0e-14 ); + t.strictEqual( y, 100.0, 'returns expected value' ); + t.end(); +}); From 571b8e9aed6f7cba9496add37d9669324bc8858b Mon Sep 17 00:00:00 2001 From: Abhist17 Date: Tue, 13 Oct 2026 12:03:38 +0530 Subject: [PATCH 2/2] fix: clamp at zero in `stats/base/dists/planck/quantile` `-log1p( -p ) / lambda` can still underflow to `0` (e.g. `p = 5e-324`, `lambda = 2`), giving `ceil( 0 ) - 1 = -1`, outside the support. Clamping at `0` like `geometric/quantile` fixes that and also returns `0` at `p = 0`. Signed-off-by: Abhist17 --- .../base/dists/planck/quantile/lib/factory.js | 3 ++- .../base/dists/planck/quantile/lib/main.js | 3 ++- .../base/dists/planck/quantile/manifest.json | 3 +++ .../base/dists/planck/quantile/src/main.c | 3 ++- .../planck/quantile/test/test.factory.js | 20 ++++++++++++++++++- .../dists/planck/quantile/test/test.native.js | 14 +++++++++++++ .../planck/quantile/test/test.quantile.js | 14 +++++++++++++ 7 files changed, 56 insertions(+), 4 deletions(-) diff --git a/lib/node_modules/@stdlib/stats/base/dists/planck/quantile/lib/factory.js b/lib/node_modules/@stdlib/stats/base/dists/planck/quantile/lib/factory.js index 22a98a30489a..5ae98175d721 100644 --- a/lib/node_modules/@stdlib/stats/base/dists/planck/quantile/lib/factory.js +++ b/lib/node_modules/@stdlib/stats/base/dists/planck/quantile/lib/factory.js @@ -24,6 +24,7 @@ var constantFunction = require( '@stdlib/utils/constant-function' ); var isnan = require( '@stdlib/math/base/assert/is-nan' ); var ceil = require( '@stdlib/math/base/special/ceil' ); var log1p = require( '@stdlib/math/base/special/log1p' ); +var max = require( '@stdlib/math/base/special/max' ); var PINF = require( '@stdlib/constants/float64/pinf' ); @@ -70,7 +71,7 @@ function factory( lambda ) { if ( p === 1.0 ) { return PINF; } - return ceil( -log1p( -p ) / lambda ) - 1.0; + return max( 0.0, ceil( -log1p( -p ) / lambda ) - 1.0 ); } } diff --git a/lib/node_modules/@stdlib/stats/base/dists/planck/quantile/lib/main.js b/lib/node_modules/@stdlib/stats/base/dists/planck/quantile/lib/main.js index f887ff548863..b1a261e483f6 100644 --- a/lib/node_modules/@stdlib/stats/base/dists/planck/quantile/lib/main.js +++ b/lib/node_modules/@stdlib/stats/base/dists/planck/quantile/lib/main.js @@ -23,6 +23,7 @@ var isnan = require( '@stdlib/math/base/assert/is-nan' ); var ceil = require( '@stdlib/math/base/special/ceil' ); var log1p = require( '@stdlib/math/base/special/log1p' ); +var max = require( '@stdlib/math/base/special/max' ); var PINF = require( '@stdlib/constants/float64/pinf' ); @@ -74,7 +75,7 @@ function quantile( p, lambda ) { if ( p === 1.0 ) { return PINF; } - return ceil( -log1p( -p ) / lambda ) - 1.0; + return max( 0.0, ceil( -log1p( -p ) / lambda ) - 1.0 ); } diff --git a/lib/node_modules/@stdlib/stats/base/dists/planck/quantile/manifest.json b/lib/node_modules/@stdlib/stats/base/dists/planck/quantile/manifest.json index 9cd530ceb45e..60de0141d359 100644 --- a/lib/node_modules/@stdlib/stats/base/dists/planck/quantile/manifest.json +++ b/lib/node_modules/@stdlib/stats/base/dists/planck/quantile/manifest.json @@ -42,6 +42,7 @@ "@stdlib/math/base/assert/is-nan", "@stdlib/math/base/special/ceil", "@stdlib/math/base/special/log1p", + "@stdlib/math/base/special/max", "@stdlib/constants/float64/pinf" ] }, @@ -60,6 +61,7 @@ "@stdlib/math/base/assert/is-nan", "@stdlib/math/base/special/ceil", "@stdlib/math/base/special/log1p", + "@stdlib/math/base/special/max", "@stdlib/constants/float64/pinf" ] }, @@ -78,6 +80,7 @@ "@stdlib/math/base/assert/is-nan", "@stdlib/math/base/special/ceil", "@stdlib/math/base/special/log1p", + "@stdlib/math/base/special/max", "@stdlib/constants/float64/pinf" ] } diff --git a/lib/node_modules/@stdlib/stats/base/dists/planck/quantile/src/main.c b/lib/node_modules/@stdlib/stats/base/dists/planck/quantile/src/main.c index 1d65f7275d1a..713a964e60da 100644 --- a/lib/node_modules/@stdlib/stats/base/dists/planck/quantile/src/main.c +++ b/lib/node_modules/@stdlib/stats/base/dists/planck/quantile/src/main.c @@ -20,6 +20,7 @@ #include "stdlib/math/base/assert/is_nan.h" #include "stdlib/math/base/special/ceil.h" #include "stdlib/math/base/special/log1p.h" +#include "stdlib/math/base/special/max.h" #include "stdlib/constants/float64/pinf.h" /** @@ -40,5 +41,5 @@ double stdlib_base_dists_planck_quantile( const double p, const double lambda ) if ( p == 1.0 ) { return STDLIB_CONSTANT_FLOAT64_PINF; } - return stdlib_base_ceil( -stdlib_base_log1p( -p ) / lambda ) - 1.0; + return stdlib_base_max( 0.0, stdlib_base_ceil( -stdlib_base_log1p( -p ) / lambda ) - 1.0 ); } diff --git a/lib/node_modules/@stdlib/stats/base/dists/planck/quantile/test/test.factory.js b/lib/node_modules/@stdlib/stats/base/dists/planck/quantile/test/test.factory.js index 4b9789a73e98..b0c6a9b69372 100644 --- a/lib/node_modules/@stdlib/stats/base/dists/planck/quantile/test/test.factory.js +++ b/lib/node_modules/@stdlib/stats/base/dists/planck/quantile/test/test.factory.js @@ -147,7 +147,7 @@ tape( 'the returned function evaluates the quantile for `p` given large paramete t.end(); }); -tape( 'the created function evaluates the quantile for small `p`', function test( t ) { +tape( 'the returned function evaluates the quantile for small `p`', function test( t ) { var quantile; var y; @@ -167,3 +167,21 @@ tape( 'the created function evaluates the quantile for small `p`', function test t.strictEqual( y, 100.0, 'returns expected value' ); t.end(); }); + +tape( 'the returned function returns `0` for `p = 0` and where `-ln(1-p)/lambda` underflows', function test( t ) { + var quantile; + var y; + + quantile = factory( 1.0 ); + y = quantile( 0.0 ); + t.strictEqual( y, 0.0, 'returns expected value' ); + + quantile = factory( 2.0 ); + y = quantile( 5.0e-324 ); + t.strictEqual( y, 0.0, 'returns expected value' ); + + quantile = factory( 1.0e300 ); + y = quantile( 1.0e-300 ); + t.strictEqual( y, 0.0, 'returns expected value' ); + t.end(); +}); diff --git a/lib/node_modules/@stdlib/stats/base/dists/planck/quantile/test/test.native.js b/lib/node_modules/@stdlib/stats/base/dists/planck/quantile/test/test.native.js index 4d4579c949ef..137c8b765ae0 100644 --- a/lib/node_modules/@stdlib/stats/base/dists/planck/quantile/test/test.native.js +++ b/lib/node_modules/@stdlib/stats/base/dists/planck/quantile/test/test.native.js @@ -135,3 +135,17 @@ tape( 'the function evaluates the quantile for small `p`', opts, function test( t.strictEqual( y, 100.0, 'returns expected value' ); t.end(); }); + +tape( 'the function returns `0` for `p = 0` and where `-ln(1-p)/lambda` underflows', opts, function test( t ) { + var y; + + y = quantile( 0.0, 1.0 ); + t.strictEqual( y, 0.0, 'returns expected value' ); + + y = quantile( 5.0e-324, 2.0 ); + t.strictEqual( y, 0.0, 'returns expected value' ); + + y = quantile( 1.0e-300, 1.0e300 ); + t.strictEqual( y, 0.0, 'returns expected value' ); + t.end(); +}); diff --git a/lib/node_modules/@stdlib/stats/base/dists/planck/quantile/test/test.quantile.js b/lib/node_modules/@stdlib/stats/base/dists/planck/quantile/test/test.quantile.js index 1d8cb47c3085..eef4c3fcee9c 100644 --- a/lib/node_modules/@stdlib/stats/base/dists/planck/quantile/test/test.quantile.js +++ b/lib/node_modules/@stdlib/stats/base/dists/planck/quantile/test/test.quantile.js @@ -126,3 +126,17 @@ tape( 'the function evaluates the quantile for small `p`', function test( t ) { t.strictEqual( y, 100.0, 'returns expected value' ); t.end(); }); + +tape( 'the function returns `0` for `p = 0` and where `-ln(1-p)/lambda` underflows', function test( t ) { + var y; + + y = quantile( 0.0, 1.0 ); + t.strictEqual( y, 0.0, 'returns expected value' ); + + y = quantile( 5.0e-324, 2.0 ); + t.strictEqual( y, 0.0, 'returns expected value' ); + + y = quantile( 1.0e-300, 1.0e300 ); + t.strictEqual( y, 0.0, 'returns expected value' ); + t.end(); +});