diff --git a/Lib/test/math_testcases.txt b/Lib/test/math_testcases.txt index 21730df7209..be1a49f0d36 100644 --- a/Lib/test/math_testcases.txt +++ b/Lib/test/math_testcases.txt @@ -84,6 +84,25 @@ erf0041 erf 1e16 -> 1.0 erf0042 erf -1e150 -> -1.0 erf0043 erf 1.7e308 -> 1.0 +-- Issue 8986: inputs x with exp(-x*x) near the underflow threshold +-- incorrectly signalled overflow on some platforms. +erf0100 erf 26.2 -> 1.0 +erf0101 erf 26.4 -> 1.0 +erf0102 erf 26.6 -> 1.0 +erf0103 erf 26.8 -> 1.0 +erf0104 erf 27.0 -> 1.0 +erf0105 erf 27.2 -> 1.0 +erf0106 erf 27.4 -> 1.0 +erf0107 erf 27.6 -> 1.0 + +erf0110 erf -26.2 -> -1.0 +erf0111 erf -26.4 -> -1.0 +erf0112 erf -26.6 -> -1.0 +erf0113 erf -26.8 -> -1.0 +erf0114 erf -27.0 -> -1.0 +erf0115 erf -27.2 -> -1.0 +erf0116 erf -27.4 -> -1.0 +erf0117 erf -27.6 -> -1.0 ---------------------------------------- -- erfc: complementary error function -- @@ -127,6 +146,25 @@ erfc0051 erfc 1e16 -> 0.0 erfc0052 erfc -1e150 -> 2.0 erfc0053 erfc 1.7e308 -> 0.0 +-- Issue 8986: inputs x with exp(-x*x) near the underflow threshold +-- incorrectly signalled overflow on some platforms. +erfc0100 erfc 26.2 -> 1.6432507924389461e-300 +erfc0101 erfc 26.4 -> 4.4017768588035426e-305 +erfc0102 erfc 26.6 -> 1.0885125885442269e-309 +erfc0103 erfc 26.8 -> 2.4849621571966629e-314 +erfc0104 erfc 27.0 -> 5.2370464393526292e-319 +erfc0105 erfc 27.2 -> 9.8813129168249309e-324 +erfc0106 erfc 27.4 -> 0.0 +erfc0107 erfc 27.6 -> 0.0 + +erfc0110 erfc -26.2 -> 2.0 +erfc0111 erfc -26.4 -> 2.0 +erfc0112 erfc -26.6 -> 2.0 +erfc0113 erfc -26.8 -> 2.0 +erfc0114 erfc -27.0 -> 2.0 +erfc0115 erfc -27.2 -> 2.0 +erfc0116 erfc -27.4 -> 2.0 +erfc0117 erfc -27.6 -> 2.0 --------------------------------------------------------- -- lgamma: log of absolute value of the gamma function -- diff --git a/Lib/test/test_math.py b/Lib/test/test_math.py index 6c4443540f3..6c07c8801db 100644 --- a/Lib/test/test_math.py +++ b/Lib/test/test_math.py @@ -1042,6 +1042,15 @@ class MathTests(unittest.TestCase): accuracy_failure = acc_check(expected, got, rel_err = 5e-15, abs_err = 5e-15) + elif fn == 'erfc': + # erfc has less-than-ideal accuracy for large + # arguments (x ~ 25 or so), mainly due to the + # error involved in computing exp(-x*x). + # + # XXX Would be better to weaken this test only + # for large x, instead of for all x. + accuracy_failure = ulps_check(expected, got, 2000) + else: accuracy_failure = ulps_check(expected, got, 20) if accuracy_failure is None: diff --git a/Misc/ACKS b/Misc/ACKS index 7f97de606d9..97952191052 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -153,6 +153,7 @@ Benjamin Collar Jeffery Collins Robert Collins Paul Colomiets +Geremy Condra Juan José Conti Matt Conway David M. Cooke diff --git a/Misc/NEWS b/Misc/NEWS index a5e0164035f..65ab474c86f 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -428,6 +428,9 @@ C-API Library ------- +- Issue #8986: math.erfc was incorrectly raising OverflowError for + values between -27.3 and -30.0 on some platforms. + - Issue #8784: Set tarfile default encoding to 'utf-8' on Windows. - Issue #8966: If a ctypes structure field is an array of c_char, convert its diff --git a/Modules/mathmodule.c b/Modules/mathmodule.c index d57ad90b107..bc3f5ddcdbd 100644 --- a/Modules/mathmodule.c +++ b/Modules/mathmodule.c @@ -428,8 +428,8 @@ m_lgamma(double x) static double m_erf_series(double x) { - double x2, acc, fk; - int i; + double x2, acc, fk, result; + int i, saved_errno; x2 = x * x; acc = 0.0; @@ -438,7 +438,12 @@ m_erf_series(double x) acc = 2.0 + x2 * acc / fk; fk -= 1.0; } - return acc * x * exp(-x2) / sqrtpi; + /* Make sure the exp call doesn't affect errno; + see m_erfc_contfrac for more. */ + saved_errno = errno; + result = acc * x * exp(-x2) / sqrtpi; + errno = saved_errno; + return result; } /* @@ -453,8 +458,8 @@ m_erf_series(double x) static double m_erfc_contfrac(double x) { - double x2, a, da, p, p_last, q, q_last, b; - int i; + double x2, a, da, p, p_last, q, q_last, b, result; + int i, saved_errno; if (x >= ERFC_CONTFRAC_CUTOFF) return 0.0; @@ -472,7 +477,12 @@ m_erfc_contfrac(double x) temp = p; p = b*p - a*p_last; p_last = temp; temp = q; q = b*q - a*q_last; q_last = temp; } - return p / q * x * exp(-x2) / sqrtpi; + /* Issue #8986: On some platforms, exp sets errno on underflow to zero; + save the current errno value so that we can restore it later. */ + saved_errno = errno; + result = p / q * x * exp(-x2) / sqrtpi; + errno = saved_errno; + return result; } /* Error function erf(x), for general x */