Skip to content

Commit dc57a61

Browse files
authored
10962 improved handling of gammaIncompleteCompleInverse edge cases (dlang#10963)
* Made NaN handling conform to that of builtin binary operators, i.e., when both input values are NaN, the one with the larger payload is returned. * Extended domain coverage to handle a = +0 * Short circuit cases where Q isn't invertible and return NaN * Explicitly handle p = 1 edge case.
1 parent cf71625 commit dc57a61

2 files changed

Lines changed: 68 additions & 8 deletions

File tree

std/internal/math/gammafunction.d

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1945,12 +1945,29 @@ do
19451945
real gammaIncompleteComplInv(real a, real p)
19461946
in
19471947
{
1948-
assert(p >= 0 && p <= 1);
1949-
assert(a>0);
1948+
if (!any!isNaN(only(a, p)))
1949+
{
1950+
assert(signbit(a) == 0);
1951+
assert(p >= 0.0L && p <= 1.0L);
1952+
}
19501953
}
19511954
do
19521955
{
1953-
if (p == 0) return real.infinity;
1956+
// pass p first, so that if p and a are NaNs with the same payload but with
1957+
// opposite signs, return p.
1958+
if (any!isNaN(only(a, p))) return largestNaNPayload(p, a);
1959+
1960+
// domain violations
1961+
if (signbit(a) == 1) return real.nan;
1962+
if (p < 0.0L || p > 1.0L) return real.nan;
1963+
1964+
// places where not invertible
1965+
if (a is +0.0L && p < 1.0L) return real.nan;
1966+
if (a is real.infinity && p > 0.0L) return real.nan;
1967+
1968+
// edge cases for p
1969+
if (p == 0.0L) return real.infinity;
1970+
if (p == 1.0L) return 0.0L;
19541971

19551972
real y0 = p;
19561973
const real MAXLOGL = 1.1356523406294143949492E4L;
@@ -2107,6 +2124,13 @@ static if (real.mant_dig >= 64) // incl. 80-bit reals
21072124
assert(fabs(gammaIncompleteCompl(100000, 100001) - 0.49831792109L) < 0.000000000005L);
21082125
else
21092126
assert(fabs(gammaIncompleteCompl(100000, 100001) - 0.49831792109L) < 0.00000005L);
2127+
2128+
assert(gammaIncompleteComplInv(NaN(0x5UL), -NaN(0x5UL)) is -NaN(0x5UL));
2129+
assert(!isNaN(gammaIncompleteComplInv(+0.0L, 1.0L)));
2130+
assert(isNaN(gammaIncompleteComplInv(+0.0L, nextDown(1.0L))));
2131+
assert(!isNaN(gammaIncompleteComplInv(real.infinity, -0.0L)));
2132+
assert(isNaN(gammaIncompleteComplInv(real.infinity, nextUp(+0.0L))));
2133+
assert(gammaIncompleteComplInv(2.0L, 1.0L) == 0.0L);
21102134
}
21112135

21122136

std/mathspecial.d

Lines changed: 41 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -505,23 +505,59 @@ do
505505
assert(isClose(gammaIncompleteCompl(1, 2), 1-gammaIncomplete(1, 2)));
506506
}
507507

508-
/** Inverse of complemented incomplete gamma integral
508+
/** Inverse regularized upper incomplete gamma function Q$(SUP -1)(a,p) with respect to p
509509
*
510-
* Given a and p, the function finds x such that
510+
* Given a and p, the function finds x such that p = Q(a,x).
511511
*
512-
* gammaIncompleteCompl( a, x ) = p.
512+
* Params:
513+
* a = the shape parameter, must be positive
514+
* p = Q(a,x), must be in the interval [0,1]
515+
*
516+
* Returns:
517+
* It returns x, a value $(GE) 0
518+
*
519+
* $(TABLE_SV
520+
* $(TR $(TH a) $(TH p) $(TH gammaIncompleteComplInverse(a, p)) )
521+
* $(TR $(TD negative) $(TD) $(TD $(NAN)) )
522+
* $(TR $(TD) $(TD $(LT) 0) $(TD $(NAN)) )
523+
* $(TR $(TD) $(TD $(GT) 1) $(TD $(NAN)) )
524+
* $(TR $(TD +0) $(TD $(LT) 1) $(TD $(NAN)) )
525+
* $(TR $(TD $(INFIN)) $(TD $(GT) 0) $(TD $(NAN)) )
526+
* $(TR $(TD $(GT) 0) $(TD 0) $(TD $(INFIN)) )
527+
* $(TR $(TD $(LT) $(INFIN)) $(TD 1) $(TD 0) )
528+
* )
529+
*
530+
* See_Also: $(LREF gammaIncompleteCompl)
513531
*/
514532
real gammaIncompleteComplInverse(real a, real p)
515533
in
516534
{
517-
assert(p >= 0 && p <= 1);
518-
assert(a > 0);
535+
// allow NaN input to pass through so that it can be addressed by the
536+
// internal NaN payload propagation logic
537+
if (!isNaN(a) && !isNaN(p))
538+
{
539+
assert(signbit(a) == 0, "a must be positive");
540+
assert(p >= 0.0L && p <= 1.0L, "p must be in the interval [0,1]");
541+
}
519542
}
543+
out(x; isNaN(x) || x >= 0.0L)
520544
do
521545
{
522546
return std.internal.math.gammafunction.gammaIncompleteComplInv(a, p);
523547
}
524548

549+
///
550+
@safe unittest
551+
{
552+
const a = 2, p = 0.5L;
553+
assert(isClose(gammaIncompleteComplInverse(a, gammaIncompleteCompl(a, p)), p));
554+
555+
assert(gammaIncompleteComplInverse(1, 1/E) == 1);
556+
assert(isNaN(gammaIncompleteComplInverse(+0.0L, 0.1)));
557+
assert(isNaN(gammaIncompleteComplInverse(real.infinity, 0.2)));
558+
assert(gammaIncompleteComplInverse(3, 0) is real.infinity);
559+
assert(gammaIncompleteComplInverse(4, 1) == 0);
560+
}
525561

526562
/* ***********************************************
527563
* ERROR FUNCTIONS & NORMAL DISTRIBUTION *

0 commit comments

Comments
 (0)