Skip to content

Commit d202b5c

Browse files
committed
pythongh-101678: refactor the math module to use special functions from c11
1 parent aacbdb0 commit d202b5c

File tree

1 file changed

+2
-163
lines changed

1 file changed

+2
-163
lines changed

Modules/mathmodule.c

+2-163
Original file line numberDiff line numberDiff line change
@@ -101,10 +101,6 @@ get_math_module_state(PyObject *module)
101101

102102
static const double pi = 3.141592653589793238462643383279502884197;
103103
static const double logpi = 1.144729885849400174143427351353058711647;
104-
#if !defined(HAVE_ERF) || !defined(HAVE_ERFC)
105-
static const double sqrtpi = 1.772453850905516027298167483341145182798;
106-
#endif /* !defined(HAVE_ERF) || !defined(HAVE_ERFC) */
107-
108104

109105
/* Version of PyFloat_AsDouble() with in-line fast paths
110106
for exact floats and integers. Gives a substantial
@@ -458,163 +454,6 @@ m_lgamma(double x)
458454
return r;
459455
}
460456

461-
#if !defined(HAVE_ERF) || !defined(HAVE_ERFC)
462-
463-
/*
464-
Implementations of the error function erf(x) and the complementary error
465-
function erfc(x).
466-
467-
Method: we use a series approximation for erf for small x, and a continued
468-
fraction approximation for erfc(x) for larger x;
469-
combined with the relations erf(-x) = -erf(x) and erfc(x) = 1.0 - erf(x),
470-
this gives us erf(x) and erfc(x) for all x.
471-
472-
The series expansion used is:
473-
474-
erf(x) = x*exp(-x*x)/sqrt(pi) * [
475-
2/1 + 4/3 x**2 + 8/15 x**4 + 16/105 x**6 + ...]
476-
477-
The coefficient of x**(2k-2) here is 4**k*factorial(k)/factorial(2*k).
478-
This series converges well for smallish x, but slowly for larger x.
479-
480-
The continued fraction expansion used is:
481-
482-
erfc(x) = x*exp(-x*x)/sqrt(pi) * [1/(0.5 + x**2 -) 0.5/(2.5 + x**2 - )
483-
3.0/(4.5 + x**2 - ) 7.5/(6.5 + x**2 - ) ...]
484-
485-
after the first term, the general term has the form:
486-
487-
k*(k-0.5)/(2*k+0.5 + x**2 - ...).
488-
489-
This expansion converges fast for larger x, but convergence becomes
490-
infinitely slow as x approaches 0.0. The (somewhat naive) continued
491-
fraction evaluation algorithm used below also risks overflow for large x;
492-
but for large x, erfc(x) == 0.0 to within machine precision. (For
493-
example, erfc(30.0) is approximately 2.56e-393).
494-
495-
Parameters: use series expansion for abs(x) < ERF_SERIES_CUTOFF and
496-
continued fraction expansion for ERF_SERIES_CUTOFF <= abs(x) <
497-
ERFC_CONTFRAC_CUTOFF. ERFC_SERIES_TERMS and ERFC_CONTFRAC_TERMS are the
498-
numbers of terms to use for the relevant expansions. */
499-
500-
#define ERF_SERIES_CUTOFF 1.5
501-
#define ERF_SERIES_TERMS 25
502-
#define ERFC_CONTFRAC_CUTOFF 30.0
503-
#define ERFC_CONTFRAC_TERMS 50
504-
505-
/*
506-
Error function, via power series.
507-
508-
Given a finite float x, return an approximation to erf(x).
509-
Converges reasonably fast for small x.
510-
*/
511-
512-
static double
513-
m_erf_series(double x)
514-
{
515-
double x2, acc, fk, result;
516-
int i, saved_errno;
517-
518-
x2 = x * x;
519-
acc = 0.0;
520-
fk = (double)ERF_SERIES_TERMS + 0.5;
521-
for (i = 0; i < ERF_SERIES_TERMS; i++) {
522-
acc = 2.0 + x2 * acc / fk;
523-
fk -= 1.0;
524-
}
525-
/* Make sure the exp call doesn't affect errno;
526-
see m_erfc_contfrac for more. */
527-
saved_errno = errno;
528-
result = acc * x * exp(-x2) / sqrtpi;
529-
errno = saved_errno;
530-
return result;
531-
}
532-
533-
/*
534-
Complementary error function, via continued fraction expansion.
535-
536-
Given a positive float x, return an approximation to erfc(x). Converges
537-
reasonably fast for x large (say, x > 2.0), and should be safe from
538-
overflow if x and nterms are not too large. On an IEEE 754 machine, with x
539-
<= 30.0, we're safe up to nterms = 100. For x >= 30.0, erfc(x) is smaller
540-
than the smallest representable nonzero float. */
541-
542-
static double
543-
m_erfc_contfrac(double x)
544-
{
545-
double x2, a, da, p, p_last, q, q_last, b, result;
546-
int i, saved_errno;
547-
548-
if (x >= ERFC_CONTFRAC_CUTOFF)
549-
return 0.0;
550-
551-
x2 = x*x;
552-
a = 0.0;
553-
da = 0.5;
554-
p = 1.0; p_last = 0.0;
555-
q = da + x2; q_last = 1.0;
556-
for (i = 0; i < ERFC_CONTFRAC_TERMS; i++) {
557-
double temp;
558-
a += da;
559-
da += 2.0;
560-
b = da + x2;
561-
temp = p; p = b*p - a*p_last; p_last = temp;
562-
temp = q; q = b*q - a*q_last; q_last = temp;
563-
}
564-
/* Issue #8986: On some platforms, exp sets errno on underflow to zero;
565-
save the current errno value so that we can restore it later. */
566-
saved_errno = errno;
567-
result = p / q * x * exp(-x2) / sqrtpi;
568-
errno = saved_errno;
569-
return result;
570-
}
571-
572-
#endif /* !defined(HAVE_ERF) || !defined(HAVE_ERFC) */
573-
574-
/* Error function erf(x), for general x */
575-
576-
static double
577-
m_erf(double x)
578-
{
579-
#ifdef HAVE_ERF
580-
return erf(x);
581-
#else
582-
double absx, cf;
583-
584-
if (Py_IS_NAN(x))
585-
return x;
586-
absx = fabs(x);
587-
if (absx < ERF_SERIES_CUTOFF)
588-
return m_erf_series(x);
589-
else {
590-
cf = m_erfc_contfrac(absx);
591-
return x > 0.0 ? 1.0 - cf : cf - 1.0;
592-
}
593-
#endif
594-
}
595-
596-
/* Complementary error function erfc(x), for general x. */
597-
598-
static double
599-
m_erfc(double x)
600-
{
601-
#ifdef HAVE_ERFC
602-
return erfc(x);
603-
#else
604-
double absx, cf;
605-
606-
if (Py_IS_NAN(x))
607-
return x;
608-
absx = fabs(x);
609-
if (absx < ERF_SERIES_CUTOFF)
610-
return 1.0 - m_erf_series(x);
611-
else {
612-
cf = m_erfc_contfrac(absx);
613-
return x > 0.0 ? cf : 2.0 - cf;
614-
}
615-
#endif
616-
}
617-
618457
/*
619458
wrapper for atan2 that deals directly with special cases before
620459
delegating to the platform libm for the remaining cases. This
@@ -1261,10 +1100,10 @@ FUNC1(cos, cos, 0,
12611100
FUNC1(cosh, cosh, 1,
12621101
"cosh($module, x, /)\n--\n\n"
12631102
"Return the hyperbolic cosine of x.")
1264-
FUNC1A(erf, m_erf,
1103+
FUNC1A(erf, erf,
12651104
"erf($module, x, /)\n--\n\n"
12661105
"Error function at x.")
1267-
FUNC1A(erfc, m_erfc,
1106+
FUNC1A(erfc, erfc,
12681107
"erfc($module, x, /)\n--\n\n"
12691108
"Complementary error function at x.")
12701109
FUNC1(exp, exp, 1,

0 commit comments

Comments
 (0)