changeset 30947:fe898ae23e0e stable

betainc.m: Use sophisticated technique for calculating exponents to avoid innacuracies (bug #62329) * betainc.m: Use expm1() and log1p() to replace exponentiation operator ".^" in special case code when a or b equals 1. Add BIST tests for bug #62329.
author Nir Krakauer <nkrakauer@ccny.cuny.edu>
date Tue, 19 Apr 2022 15:09:27 -0700
parents 9c7822f4fc9c
children 74f8854958d2 7d99816e9709
files scripts/specfun/betainc.m
diffstat 1 files changed, 15 insertions(+), 3 deletions(-) [+]
line wrap: on
line diff
--- a/scripts/specfun/betainc.m	Sat Apr 16 12:43:24 2022 -0700
+++ b/scripts/specfun/betainc.m	Tue Apr 19 15:09:27 2022 -0700
@@ -139,12 +139,16 @@
 
   if (strcmpi (tail, "lower"))
     y(a_b_one) = x(a_b_one);
-    y(a_one) = 1 - (1 - x(a_one)) .^ b(a_one);
+    ## See bug #62329.
+    ## equivalent to "1 - (1 - x(a_one)) .^ b(a_one)", but less roundoff error
+    y(a_one) = - expm1 (log1p (- x(a_one)) .* b(a_one));
     y(b_one) = x(b_one) .^ a(b_one);
   elseif (strcmpi (tail, "upper"))
     y(a_b_one) = 1 - x(a_b_one);
-    y(a_one) = (1 - x(a_one)) .^ b(a_one);
-    y(b_one) = 1 - x(b_one) .^ a(b_one);
+    ## equivalent to "(1 - x(a_one)) .^ b(a_one)", but less roundoff error
+    y(a_one) = exp (log1p (- x(a_one)) .* b(a_one));
+    ## equivalent to "1 - x(b_one) .^ a(b_one)", but less roundoff error
+    y(b_one) = - expm1 (log (x(b_one)) .* a(b_one));
   endif
 
   ## Non-Trivial cases
@@ -245,11 +249,19 @@
 %! [a,b] = ndgrid (linspace (1e-4, 100, 20), linspace (1e-4, 100, 20));
 %! assert (betainc (0, a, b), zeros (20));
 %! assert (betainc (1, a, b), ones (20));
+%! assert (betainc (0, a, b, "upper"), ones (20));
+%! assert (betainc (1, a, b, "upper"), zeros (20));
 
 %!test <*34405>
 %! assert (betainc (NaN, 1, 2), NaN);
 %! assert (betainc (0.5, 1, Inf), 1);
 
+%!test <*62329>
+%! assert (betainc (2e-20, 1, 0.5), 1e-20, -1e-15)
+%! assert (betainc (2e-5, 1, 0.5), 2e-5 / (1 + sqrt (1 - 2e-5)), -1e-15)
+%! assert (betainc (0.99, 1, 0.5, "upper"), 0.1, -1e-15)
+%! assert (betainc (0.99, 0.5, 1, "upper"), - expm1 (log (0.99)/2), -1e-15)
+
 ## Test input validation
 %!error <Invalid call> betainc ()
 %!error <Invalid call> betainc (1)