Skip to content

fix: evaluate the tails from p and 1-p in stats/base/dists/cauchy/quantile - #15778

Open
Abhist17 wants to merge 1 commit into
stdlib-js:developfrom
Abhist17:fix/cauchy-quantile-tails
Open

Abhist17 wants to merge 1 commit into
stdlib-js:developfrom
Abhist17:fix/cauchy-quantile-tails

Conversation

@Abhist17

@Abhist17 Abhist17 commented Oct 2, 2026

Copy link
Copy Markdown
Contributor

Description

What is the purpose of this pull request?

This pull request:

  • evaluates the tails of stats/base/dists/cauchy/quantile from p and 1-p (JS, factory and C).

The function computed x0 + gamma*tan( PI*(p-0.5) ). For p close to 0, p - 0.5 rounds away the low-order bits of p. The lower tail is then wrong by orders of magnitude, and the endpoints don't reach infinity:

var quantile = require( '@stdlib/stats/base/dists/cauchy/quantile' );

quantile( 1.0e-20, 0.0, 1.0 );
// develop: -16331239353195370
// this PR: -31830988618379070000 (= -1/(pi*1e-20))

quantile( 1.0e-10, 0.0, 1.0 );
// develop: -3183097229.936138
// this PR: -3183098861.8379064

quantile( 0.0, 0.0, 1.0 );
// develop: -16331239353195370
// this PR: -Infinity

quantile( 1.0, 0.0, 1.0 );
// develop: 16331239353195370
// this PR: Infinity

Since tan(pi*(p - 0.5)) = -1/tan(pi*p), the function now uses:

  • x0 - gamma/tan(PI*p) for p < 0.25,
  • x0 + gamma/tan(PI*(1-p)) for p > 0.75, where 1-p is exact,
  • the old form in between, where p - 0.5 is exact.

Fixtures and tests:

  • runner.jl now evaluates the textbook formula in 2048-bit BigFloat instead of Distributions.quantile, which has the same p - 0.5 loss. Distributions is dropped from REQUIRE, and the three fixtures are regenerated with the same input ranges.

  • There's a new tails.json: 500 points with p log-spaced over [1e-300, 0.1] and 500 with 1-p log-spaced over [1e-16, 0.1]. x0 is 0 there, to isolate the tail.

  • The tests used an EPS-relative delta (50 EPS). They now use isAlmostSameValue with the smallest passing tolerances, the same in JS and native:

    fixture develop this PR
    large_gamma 186 ULP 28 ULP
    positive_median 3,205 ULP 162 ULP
    negative_median 639 ULP 326 ULP
    tails wrong by orders of magnitude 2 ULP

    What's left in the first three comes from x0 + gamma*tan(...) cancelling when the two terms nearly cancel, e.g. -9.28 + 9.29. That's the conditioning of the sum, not the tail. On develop, 999 of the 4,015 assertions in test.quantile.js fail. With this change they all pass.

Related Issues

Does this pull request have any related issues?

None. Same kind of fix as #15773 (laplace/quantile).

Questions

Any questions for reviewers of this pull request?

No.

Other

Any other information relevant to this pull request? This may include screenshots, references, and/or implementation notes.

The comment in the old large_gamma test about Julia's and stdlib's tan disagreeing is gone. The fixtures no longer use Julia's double-precision tan.

Checklist

Please ensure the following tasks are completed before submitting this pull request.

AI Assistance

When authoring the changes proposed in this PR, did you use any kind of AI assistance?

  • Yes
  • No

If you answered "yes" above, how did you use AI assistance?

  • Code generation (e.g., when writing an implementation or fixing a bug)
  • Test/benchmark generation
  • Documentation (including examples)
  • Research and understanding

Disclosure

If you answered "yes" to using AI assistance, please provide a short disclosure indicating how you used AI assistance. This helps reviewers determine how much scrutiny to apply when reviewing your contribution. Example disclosures: "This PR was written primarily by Claude Code." or "I consulted ChatGPT to understand the codebase, but the proposed changes were fully authored manually by myself.".

This PR was written primarily by Claude Code. I found the bug by checking stats/base/dists quantile functions at tiny p.


@stdlib-js/reviewers

…y/quantile`

`tan( PI*(p-0.5) )` rounds away the low-order bits of a `p` close to
`0`: the lower tail is wrong by orders of magnitude for small `p`, and
`p = 0` and `p = 1` return `-/+1.6e16` instead of `-/+Infinity`. Use
`-1/tan(PI*p)` for `p < 0.25` and `1/tan(PI*(1-p))` for `p > 0.75`. The
fixtures are now generated from the textbook formula in extended
precision and the tests use ULP-based assertions.
@Abhist17
Abhist17 requested a review from a team October 2, 2026 00:04
@stdlib-bot stdlib-bot added Statistics Issue or pull request related to statistical functionality. Needs Review A pull request which needs code review. labels Oct 2, 2026
@stdlib-bot

Copy link
Copy Markdown
Contributor

Coverage Report

Package Statements Branches Functions Lines
stats/base/dists/cauchy/quantile $\\color{green}320/320$
$\\color{green}+100.00\\%$
$\\color{green}31/31$
$\\color{green}+100.00\\%$
$\\color{green}4/4$
$\\color{green}+100.00\\%$
$\\color{green}320/320$
$\\color{green}+100.00\\%$

The above coverage report was generated for the changes in this PR.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Needs Review A pull request which needs code review. Statistics Issue or pull request related to statistical functionality.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants