Repository navigation
Conversation
…y/cdf` `1 - x^a` rounds away the low-order bits of `x^a`, and subtracting `(1 - x^a)^b` from `1` then cancels: for random inputs only about four digits were right, and for `x^a < 2^-53` the function returned `0`. Use `-expm1( b * log1p( -x^a ) )` and add fixtures generated from the textbook formula in extended precision.
Coverage Report
The above coverage report was generated for the changes in this PR. |
| return 1.0; | ||
| } | ||
| return 1.0 - pow( 1.0 - pow( x, a ), b ); | ||
| return -expm1( b * log1p( -pow( x, a ) ) ); |
There was a problem hiding this comment.
| return -expm1( b * log1p( -pow( x, a ) ) ); | |
| u = pow( x, a ); | |
| if ( u < 0.5 ) { | |
| return -expm1( b * log1p( -u ) ); | |
| } | |
| return -expm1( b * ln( -expm1( a * ln( x ) ) ) ); |
When x^a is close to 1, pow( x, a ) rounds before log1p: cdf( 0.999999999999999, 1.3786, 0.0841 ) returns 0.943898… (exact 0.943740…, ~1.4e12 ULP, same on develop). #15768 already splits on u < 0.5 for logcdf; with the same split here the max error is 2 ULP across the range. Could you fold it in? Same in lib/factory.js:80 and src/main.c:53; needs var u; and ln.
…/cdf` Per review: when `x^a` is close to 1, `pow( x, a )` rounds before `log1p`, so `cdf( 0.999999999999999, 1.3786, 0.0841 )` gave 0.943898 (exact 0.943740). Split on `u < 0.5` as `logcdf` does and take `ln( -expm1( a*ln(x) ) )` above.
|
@0PrashantYadav0 folded in, 1adfd59: the |
0PrashantYadav0
left a comment
There was a problem hiding this comment.
LGTM. Non-blocking: no fixture reaches x near 1 yet (data.json stops at 0.99908), so the new u >= 0.5 branch isn't tested where the old path lost accuracy. Maybe add x = 1.0 .- exp10.( range( -16.0, stop = -1.0, length = 1000 ) ) to runner.jl?
Per review: no fixture reached the `u >= 0.5` branch where the old path lost accuracy. Adds `large_x.json` with `x = 1 - 10^k`, `k` in [-16, -1]. This branch is within 1 ULP; develop is over 2 ULP on 51 of the points (max 23,412).
|
@0PrashantYadav0 added in fa51bb0: |
Description
This pull request:
stats/base/dists/kumaraswamy/cdfas-expm1( b * log1p( -pow( x, a ) ) )instead of1 - pow( 1 - pow( x, a ), b )(JS, factory and C),TODOfor.Forming
1 - x^arounds away the low-order bits ofx^a, and subtracting(1 - x^a)^bfrom1then cancels. Whenever the CDF is small, most of the digits are wrong. Forx^a < 2^-53the result is exactly0:Tests:
test/fixtures/julia/runner.jlthat evaluates the textbook formula1 - (1 - x^a)^bin 2048-bitBigFloat. It writesdata.json(randomxin(0,1),aandbin[0.5, 5.5]) andsmall_x.json(xlog-spaced over[1e-100, 0.1]).test.cdf.js,test.factory.jsandtest.native.jsonly checked that the result is a number (// TODO: Add fixtures). They are replaced with tests against the two fixtures at 2 ULP. That's the max error in both JS and native.develop, the max error is about 6.4e10 ULP ondata.json, and0where the CDF is around1e-16onsmall_x.json. 1,220 of the 2,127 assertions intest.cdf.jsfail. With this change they all pass.Related Issues
None.
Questions
kumaraswamy/logcdfandkumaraswamy/quantilehave the same cancellation. I'll send them separately.Other
No.
Checklist
AI Assistance
If you answered "yes" above, how did you use AI assistance?
Disclosure
This PR was written primarily by Claude Code. I found the bug by reading the
stats/base/distsfunctions for1 - ucancellation.@stdlib-js/reviewers