Index: Misc/NEWS
===================================================================
--- Misc/NEWS (revision 76762)
+++ Misc/NEWS (working copy)
@@ -1654,7 +1654,7 @@
- Issue #7078: Set struct.__doc__ from _struct.__doc__.
-- Issue #3366: Add gamma, lgamma functions to math module.
+- Issue #3366: Add erf, erfc, gamma, lgamma functions to math module.
- Issue #6823: Allow time.strftime() to accept a tuple with a isdst field
outside of the range of [-1, 1] by normalizing the value to within that
Index: Doc/library/math.rst
===================================================================
--- Doc/library/math.rst (revision 76762)
+++ Doc/library/math.rst (working copy)
@@ -311,6 +311,20 @@
Special functions
-----------------
+.. function:: erf(x)
+
+ Return the error function at *x*.
+
+ .. versionadded:: 2.7
+
+
+.. function:: erfc(x)
+
+ Return the complementary error function at *x*.
+
+ .. versionadded:: 2.7
+
+
.. function:: gamma(x)
Return the Gamma function at *x*.
Index: Lib/erf.py
===================================================================
--- Lib/erf.py (revision 0)
+++ Lib/erf.py (revision 0)
@@ -0,0 +1,114 @@
+# Implementations of the error and complementary error functions.
+
+from math import copysign, exp, isnan, isinf
+
+# Method: following 'Numerical Recipes' by Flannery, Press
+# et. al. (2nd ed., Cambridge University Press), we use a series
+# approximation for erf for small x, and a continued fraction
+# approximation for erfc(x) for larger x; combined with the relations
+# erf(-x) = -erf(x) and erfc(x) = 1.0 - erf(x), this gives us erf(x)
+# and erfc(x) for all x.
+
+# The series expansion used is:
+#
+# erf(x) = x*exp(-x*x)/sqrt(pi) * [2/1 + 4/3 x**2 + 8/15 x**4 + 16/105 x**6 + ...]
+#
+# The coefficient of x**(2k-2) here is 4**k*factorial(k)/factorial(2*k). This series
+# converges well for smallish x, but slowly for larger x.
+
+# The continued fraction expansion used is:
+#
+# erfc(x) = x*exp(-x*x)/sqrt(pi) * [1/(0.5 + x**2 -) 0.5/(2.5 + x**2 - )
+# 3.0/(4.5 + x**2 - ) 7.5/(6.5 + x**2 - ) ...]
+#
+# after the first term, the general term has the form:
+#
+# k*(k-0.5)/(2*k+0.5 + x**2 - ...).
+#
+# This expansion converges fast for larger x; convergence becomes
+# infinitely slow as x approaches 0.0. The (somewhat naive) continued
+# fraction evaluation algorithm used below also risks overflow for
+# large x; but for large x, erfc(x) == 0.0 to within machine
+# precision. (For example, erfc(30.0) is approximately 2.56e-393).
+
+# Parameters: use series expansion for abs(x) < SERIES_CUTOFF and
+# continued fraction expansion for SERIES_CUTOFF <= abs(x) <
+# CONTFRAC_CUTOFF. SERIES_TERMS and CONTFRAC_TERMS are the default
+# numbers of terms to use for the relevant expansions.
+SERIES_CUTOFF = 1.5
+SERIES_TERMS = 25
+CONTFRAC_CUTOFF = 30.0
+CONTFRAC_TERMS = 50
+
+SQRTPI = 1.7724538509055161
+
+def erf_series(x, nterms=SERIES_TERMS):
+ """Error function, via power series.
+
+ Given a finite float x, return an approximation to erf(x).
+ Converges reasonably fast for x small (say abs(x) < 1).
+ """
+
+ x2 = x*x
+ acc = 0.0
+ fk = float(nterms) + 0.5
+ for i in xrange(nterms):
+ acc = 2.0 + x2 * acc / fk
+ fk -= 1.0
+ return x * exp(-x2) / SQRTPI * acc
+
+def erfc_cont_frac(x, nterms=CONTFRAC_TERMS):
+ """Complementary error function, via continued fraction expansion.
+
+ Given a positive float x, return an approximation to erfc(x).
+ Converges reasonably fast for x large (say, x > 2.0), and should
+ be safe from overflow if x and nterms are not too large. On an
+ IEEE 754 machine, with x <= 30.0, we're safe up to nterms = 100.
+ For x >= 30.0, erfc(x) is smaller than the smallest representable
+ nonzero float.
+
+ """
+
+ if x >= CONTFRAC_CUTOFF:
+ return 0.0
+
+ x2 = x * x
+ pkm1, qkm1 = 0.0, 1.0
+ pk, qk = 1, 0.5 + x2
+ for i in xrange(1, nterms):
+ ak = -i*(i-0.5)
+ bk = (4*i+1.0)/2.0 + x2
+ pk, pkm1 = bk*pk + ak*pkm1, pk
+ qk, qkm1 = bk*qk + ak*qkm1, qk
+ return x * exp(-x2) / SQRTPI * (pk / qk)
+
+def erf(x):
+ """Error function of x."""
+
+ if isnan(x):
+ return x
+
+ absx = abs(x)
+ if absx < SERIES_CUTOFF:
+ return erf_series(x)
+ else:
+ result = 1.0 - erfc_cont_frac(absx)
+ return result if x > 0.0 else -result
+
+def erfc(x):
+ """Complementary error function of x."""
+
+ if isnan(x):
+ return x
+
+ absx = abs(x)
+ if absx < SERIES_CUTOFF:
+ return 1.0 - erf_series(x)
+ else:
+ result = erfc_cont_frac(absx)
+ return result if x > 0.0 else 2.0 - result
+
+
+# Get corresponding correctly rounded functions from bigfloat module,
+# for comparison.
+#from bigfloat import erf as erf_bigfloat, erfc as erfc_bigfloat
Index: Lib/test/test_math.py
===================================================================
--- Lib/test/test_math.py (revision 76762)
+++ Lib/test/test_math.py (working copy)
@@ -4,6 +4,7 @@
from test.test_support import run_unittest, verbose
import unittest
import math
+import erf
import os
import sys
import random
@@ -968,7 +969,10 @@
failures = []
for id, fn, arg, expected, flags in parse_mtestfile(math_testcases):
- func = getattr(math, fn)
+ try:
+ func = getattr(math, fn)
+ except AttributeError:
+ func = getattr(erf, fn)
if 'invalid' in flags or 'divide-by-zero' in flags:
expected = 'ValueError'
@@ -989,7 +993,7 @@
if not math.isnan(expected) and not math.isnan(got):
# we use different closeness criteria for
# different functions.
- if fn == 'gamma':
+ if fn in ('gamma', 'erf', 'erfc'):
accuracy_failure = ulps_check(expected, got, 20)
elif fn == 'lgamma':
accuracy_failure = acc_check(expected, got,
Index: Lib/test/math_testcases.txt
===================================================================
--- Lib/test/math_testcases.txt (revision 76762)
+++ Lib/test/math_testcases.txt (working copy)
@@ -47,6 +47,85 @@
-- MPFR homepage at http://www.mpfr.org for more information about the
-- MPFR project.
+-------------------------
+-- erf: error function --
+-------------------------
+
+erf0000 erf 0.0 -> 0.0
+erf0001 erf -0.0 -> -0.0
+erf0002 erf inf -> 1.0
+erf0003 erf -inf -> -1.0
+erf0004 erf nan -> nan
+
+-- tiny values
+erf0010 erf 1e-308 -> 1.1283791670955125e-308
+erf0011 erf 5e-324 -> 4.9406564584124654e-324
+erf0012 erf 1e-10 -> 1.1283791670955126e-10
+
+-- small integers
+erf0020 erf 1 -> 0.84270079294971489
+erf0021 erf 2 -> 0.99532226501895271
+erf0022 erf 3 -> 0.99997790950300136
+erf0023 erf 4 -> 0.99999998458274209
+erf0024 erf 5 -> 0.99999999999846256
+erf0025 erf 6 -> 1.0
+
+erf0030 erf -1 -> -0.84270079294971489
+erf0031 erf -2 -> -0.99532226501895271
+erf0032 erf -3 -> -0.99997790950300136
+erf0033 erf -4 -> -0.99999998458274209
+erf0034 erf -5 -> -0.99999999999846256
+erf0035 erf -6 -> -1.0
+
+-- huge values should all go to +/-1, depending on sign
+erf0040 erf -40 -> -1.0
+erf0041 erf 1e16 -> 1.0
+erf0042 erf -1e150 -> -1.0
+erf0043 erf 1.7e308 -> 1.0
+
+
+----------------------------------------
+-- erfc: complementary error function --
+----------------------------------------
+
+erfc0000 erfc 0.0 -> 1.0
+erfc0001 erfc -0.0 -> 1.0
+erfc0002 erfc inf -> 0.0
+erfc0003 erfc -inf -> 2.0
+erfc0004 erfc nan -> nan
+
+-- tiny values
+erfc0010 erfc 1e-308 -> 1.0
+erfc0011 erfc 5e-324 -> 1.0
+erfc0012 erfc 1e-10 -> 0.99999999988716204
+
+-- small integers
+erfc0020 erfc 1 -> 0.15729920705028513
+erfc0021 erfc 2 -> 0.0046777349810472662
+erfc0022 erfc 3 -> 2.2090496998585441e-05
+erfc0023 erfc 4 -> 1.541725790028002e-08
+erfc0024 erfc 5 -> 1.5374597944280349e-12
+erfc0025 erfc 6 -> 2.1519736712498913e-17
+
+erfc0030 erfc -1 -> 1.8427007929497148
+erfc0031 erfc -2 -> 1.9953222650189528
+erfc0032 erfc -3 -> 1.9999779095030015
+erfc0033 erfc -4 -> 1.9999999845827421
+erfc0034 erfc -5 -> 1.9999999999984626
+erfc0035 erfc -6 -> 2.0
+
+-- as x -> infinity, erfc(x) behaves like exp(-x*x)/x/sqrt(pi)
+erfc0040 erfc 20 -> 5.3958656116079012e-176
+erfc0041 erfc 25 -> 8.3001725711965228e-274
+erfc0042 erfc 27 -> 5.2370464393526292e-319
+erfc0043 erfc 28 -> 0.0
+
+-- huge values
+erfc0050 erfc -40 -> 2.0
+erfc0051 erfc 1e16 -> 0.0
+erfc0052 erfc -1e150 -> 2.0
+erfc0053 erfc 1.7e308 -> 0.0
+
---------------------------------------------------------
-- lgamma: log of absolute value of the gamma function --
---------------------------------------------------------