comparison scripts/specfun/betainc.m @ 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 796f54d4ddbf
children 74f8854958d2
comparison
equal deleted inserted replaced
30938:9c7822f4fc9c 30947:fe898ae23e0e
137 a_one &= b_not_one; 137 a_one &= b_not_one;
138 b_one &= a_not_one; 138 b_one &= a_not_one;
139 139
140 if (strcmpi (tail, "lower")) 140 if (strcmpi (tail, "lower"))
141 y(a_b_one) = x(a_b_one); 141 y(a_b_one) = x(a_b_one);
142 y(a_one) = 1 - (1 - x(a_one)) .^ b(a_one); 142 ## See bug #62329.
143 ## equivalent to "1 - (1 - x(a_one)) .^ b(a_one)", but less roundoff error
144 y(a_one) = - expm1 (log1p (- x(a_one)) .* b(a_one));
143 y(b_one) = x(b_one) .^ a(b_one); 145 y(b_one) = x(b_one) .^ a(b_one);
144 elseif (strcmpi (tail, "upper")) 146 elseif (strcmpi (tail, "upper"))
145 y(a_b_one) = 1 - x(a_b_one); 147 y(a_b_one) = 1 - x(a_b_one);
146 y(a_one) = (1 - x(a_one)) .^ b(a_one); 148 ## equivalent to "(1 - x(a_one)) .^ b(a_one)", but less roundoff error
147 y(b_one) = 1 - x(b_one) .^ a(b_one); 149 y(a_one) = exp (log1p (- x(a_one)) .* b(a_one));
150 ## equivalent to "1 - x(b_one) .^ a(b_one)", but less roundoff error
151 y(b_one) = - expm1 (log (x(b_one)) .* a(b_one));
148 endif 152 endif
149 153
150 ## Non-Trivial cases 154 ## Non-Trivial cases
151 ## In the following, we use the fact that the continued fraction Octave uses 155 ## In the following, we use the fact that the continued fraction Octave uses
152 ## is more efficient when x <= a / (a + b). Moreover, to compute the upper 156 ## is more efficient when x <= a / (a + b). Moreover, to compute the upper
243 ## Test trivial values 247 ## Test trivial values
244 %!test 248 %!test
245 %! [a,b] = ndgrid (linspace (1e-4, 100, 20), linspace (1e-4, 100, 20)); 249 %! [a,b] = ndgrid (linspace (1e-4, 100, 20), linspace (1e-4, 100, 20));
246 %! assert (betainc (0, a, b), zeros (20)); 250 %! assert (betainc (0, a, b), zeros (20));
247 %! assert (betainc (1, a, b), ones (20)); 251 %! assert (betainc (1, a, b), ones (20));
252 %! assert (betainc (0, a, b, "upper"), ones (20));
253 %! assert (betainc (1, a, b, "upper"), zeros (20));
248 254
249 %!test <*34405> 255 %!test <*34405>
250 %! assert (betainc (NaN, 1, 2), NaN); 256 %! assert (betainc (NaN, 1, 2), NaN);
251 %! assert (betainc (0.5, 1, Inf), 1); 257 %! assert (betainc (0.5, 1, Inf), 1);
258
259 %!test <*62329>
260 %! assert (betainc (2e-20, 1, 0.5), 1e-20, -1e-15)
261 %! assert (betainc (2e-5, 1, 0.5), 2e-5 / (1 + sqrt (1 - 2e-5)), -1e-15)
262 %! assert (betainc (0.99, 1, 0.5, "upper"), 0.1, -1e-15)
263 %! assert (betainc (0.99, 0.5, 1, "upper"), - expm1 (log (0.99)/2), -1e-15)
252 264
253 ## Test input validation 265 ## Test input validation
254 %!error <Invalid call> betainc () 266 %!error <Invalid call> betainc ()
255 %!error <Invalid call> betainc (1) 267 %!error <Invalid call> betainc (1)
256 %!error <Invalid call> betainc (1,2) 268 %!error <Invalid call> betainc (1,2)