mirror of
https://github.com/python/cpython.git
synced 2025-11-01 06:01:29 +00:00
The bug report title isn't correct, but was on the right track. Rev 2.13 applied a patch intended to improve asinh and acosh, but the author mistakenly replaced the body of asin with their new code for asinh. See bug report for all the gory details. This patch: (a) puts the "new" (as of 2.13) asinh code into the asinh function; and, (b) repairs asin via what Abramowitz & Stegun say it should be (which is probably the same as what 2.12 did for asin, although I got tired of matching parentheses before being 100% sure of that -- and I don't care! The source of the old code is a mystery, and I *know* why I picked the new code.).
419 lines
7.8 KiB
C
419 lines
7.8 KiB
C
/* Complex math module */
|
|
|
|
/* much code borrowed from mathmodule.c */
|
|
|
|
#include "Python.h"
|
|
|
|
#ifdef i860
|
|
/* Cray APP has bogus definition of HUGE_VAL in <math.h> */
|
|
#undef HUGE_VAL
|
|
#endif
|
|
|
|
#ifdef HUGE_VAL
|
|
#define CHECK(x) if (errno != 0) ; \
|
|
else if (-HUGE_VAL <= (x) && (x) <= HUGE_VAL) ; \
|
|
else errno = ERANGE
|
|
#else
|
|
#define CHECK(x) /* Don't know how to check */
|
|
#endif
|
|
|
|
#ifndef M_PI
|
|
#define M_PI (3.141592653589793239)
|
|
#endif
|
|
|
|
/* First, the C functions that do the real work */
|
|
|
|
/* constants */
|
|
static Py_complex c_one = {1., 0.};
|
|
static Py_complex c_half = {0.5, 0.};
|
|
static Py_complex c_i = {0., 1.};
|
|
static Py_complex c_halfi = {0., 0.5};
|
|
|
|
/* forward declarations */
|
|
staticforward Py_complex c_log(Py_complex);
|
|
staticforward Py_complex c_prodi(Py_complex);
|
|
staticforward Py_complex c_sqrt(Py_complex);
|
|
|
|
|
|
static Py_complex
|
|
c_acos(Py_complex x)
|
|
{
|
|
return c_neg(c_prodi(c_log(c_sum(x,c_prod(c_i,
|
|
c_sqrt(c_diff(c_one,c_prod(x,x))))))));
|
|
}
|
|
|
|
static char c_acos_doc[] =
|
|
"acos(x)\n"
|
|
"\n"
|
|
"Return the arc cosine of x.";
|
|
|
|
|
|
static Py_complex
|
|
c_acosh(Py_complex x)
|
|
{
|
|
Py_complex z;
|
|
z = c_sqrt(c_half);
|
|
z = c_log(c_prod(z, c_sum(c_sqrt(c_sum(x,c_one)),
|
|
c_sqrt(c_diff(x,c_one)))));
|
|
return c_sum(z, z);
|
|
}
|
|
|
|
static char c_acosh_doc[] =
|
|
"acosh(x)\n"
|
|
"\n"
|
|
"Return the hyperbolic arccosine of x.";
|
|
|
|
|
|
static Py_complex
|
|
c_asin(Py_complex x)
|
|
{
|
|
/* -i * log[(sqrt(1-x**2) + i*x] */
|
|
const Py_complex squared = c_prod(x, x);
|
|
const Py_complex sqrt_1_minus_x_sq = c_sqrt(c_diff(c_one, squared));
|
|
const Py_complex sum = c_sum(sqrt_1_minus_x_sq, c_prod(c_i, x));
|
|
return c_neg(c_prodi(c_log(sum)));
|
|
}
|
|
|
|
static char c_asin_doc[] =
|
|
"asin(x)\n"
|
|
"\n"
|
|
"Return the arc sine of x.";
|
|
|
|
|
|
static Py_complex
|
|
c_asinh(Py_complex x)
|
|
{
|
|
Py_complex z;
|
|
z = c_sqrt(c_half);
|
|
z = c_log(c_prod(z, c_sum(c_sqrt(c_sum(x, c_i)),
|
|
c_sqrt(c_diff(x, c_i)))));
|
|
return c_sum(z, z);
|
|
}
|
|
|
|
static char c_asinh_doc[] =
|
|
"asinh(x)\n"
|
|
"\n"
|
|
"Return the hyperbolic arc sine of x.";
|
|
|
|
|
|
static Py_complex
|
|
c_atan(Py_complex x)
|
|
{
|
|
return c_prod(c_halfi,c_log(c_quot(c_sum(c_i,x),c_diff(c_i,x))));
|
|
}
|
|
|
|
static char c_atan_doc[] =
|
|
"atan(x)\n"
|
|
"\n"
|
|
"Return the arc tangent of x.";
|
|
|
|
|
|
static Py_complex
|
|
c_atanh(Py_complex x)
|
|
{
|
|
return c_prod(c_half,c_log(c_quot(c_sum(c_one,x),c_diff(c_one,x))));
|
|
}
|
|
|
|
static char c_atanh_doc[] =
|
|
"atanh(x)\n"
|
|
"\n"
|
|
"Return the hyperbolic arc tangent of x.";
|
|
|
|
|
|
static Py_complex
|
|
c_cos(Py_complex x)
|
|
{
|
|
Py_complex r;
|
|
r.real = cos(x.real)*cosh(x.imag);
|
|
r.imag = -sin(x.real)*sinh(x.imag);
|
|
return r;
|
|
}
|
|
|
|
static char c_cos_doc[] =
|
|
"cos(x)\n"
|
|
"n"
|
|
"Return the cosine of x.";
|
|
|
|
|
|
static Py_complex
|
|
c_cosh(Py_complex x)
|
|
{
|
|
Py_complex r;
|
|
r.real = cos(x.imag)*cosh(x.real);
|
|
r.imag = sin(x.imag)*sinh(x.real);
|
|
return r;
|
|
}
|
|
|
|
static char c_cosh_doc[] =
|
|
"cosh(x)\n"
|
|
"n"
|
|
"Return the hyperbolic cosine of x.";
|
|
|
|
|
|
static Py_complex
|
|
c_exp(Py_complex x)
|
|
{
|
|
Py_complex r;
|
|
double l = exp(x.real);
|
|
r.real = l*cos(x.imag);
|
|
r.imag = l*sin(x.imag);
|
|
return r;
|
|
}
|
|
|
|
static char c_exp_doc[] =
|
|
"exp(x)\n"
|
|
"\n"
|
|
"Return the exponential value e**x.";
|
|
|
|
|
|
static Py_complex
|
|
c_log(Py_complex x)
|
|
{
|
|
Py_complex r;
|
|
double l = hypot(x.real,x.imag);
|
|
r.imag = atan2(x.imag, x.real);
|
|
r.real = log(l);
|
|
return r;
|
|
}
|
|
|
|
static char c_log_doc[] =
|
|
"log(x)\n"
|
|
"\n"
|
|
"Return the natural logarithm of x.";
|
|
|
|
|
|
static Py_complex
|
|
c_log10(Py_complex x)
|
|
{
|
|
Py_complex r;
|
|
double l = hypot(x.real,x.imag);
|
|
r.imag = atan2(x.imag, x.real)/log(10.);
|
|
r.real = log10(l);
|
|
return r;
|
|
}
|
|
|
|
static char c_log10_doc[] =
|
|
"log10(x)\n"
|
|
"\n"
|
|
"Return the base-10 logarithm of x.";
|
|
|
|
|
|
/* internal function not available from Python */
|
|
static Py_complex
|
|
c_prodi(Py_complex x)
|
|
{
|
|
Py_complex r;
|
|
r.real = -x.imag;
|
|
r.imag = x.real;
|
|
return r;
|
|
}
|
|
|
|
|
|
static Py_complex
|
|
c_sin(Py_complex x)
|
|
{
|
|
Py_complex r;
|
|
r.real = sin(x.real) * cosh(x.imag);
|
|
r.imag = cos(x.real) * sinh(x.imag);
|
|
return r;
|
|
}
|
|
|
|
static char c_sin_doc[] =
|
|
"sin(x)\n"
|
|
"\n"
|
|
"Return the sine of x.";
|
|
|
|
|
|
static Py_complex
|
|
c_sinh(Py_complex x)
|
|
{
|
|
Py_complex r;
|
|
r.real = cos(x.imag) * sinh(x.real);
|
|
r.imag = sin(x.imag) * cosh(x.real);
|
|
return r;
|
|
}
|
|
|
|
static char c_sinh_doc[] =
|
|
"sinh(x)\n"
|
|
"\n"
|
|
"Return the hyperbolic sine of x.";
|
|
|
|
|
|
static Py_complex
|
|
c_sqrt(Py_complex x)
|
|
{
|
|
Py_complex r;
|
|
double s,d;
|
|
if (x.real == 0. && x.imag == 0.)
|
|
r = x;
|
|
else {
|
|
s = sqrt(0.5*(fabs(x.real) + hypot(x.real,x.imag)));
|
|
d = 0.5*x.imag/s;
|
|
if (x.real > 0.) {
|
|
r.real = s;
|
|
r.imag = d;
|
|
}
|
|
else if (x.imag >= 0.) {
|
|
r.real = d;
|
|
r.imag = s;
|
|
}
|
|
else {
|
|
r.real = -d;
|
|
r.imag = -s;
|
|
}
|
|
}
|
|
return r;
|
|
}
|
|
|
|
static char c_sqrt_doc[] =
|
|
"sqrt(x)\n"
|
|
"\n"
|
|
"Return the square root of x.";
|
|
|
|
|
|
static Py_complex
|
|
c_tan(Py_complex x)
|
|
{
|
|
Py_complex r;
|
|
double sr,cr,shi,chi;
|
|
double rs,is,rc,ic;
|
|
double d;
|
|
sr = sin(x.real);
|
|
cr = cos(x.real);
|
|
shi = sinh(x.imag);
|
|
chi = cosh(x.imag);
|
|
rs = sr * chi;
|
|
is = cr * shi;
|
|
rc = cr * chi;
|
|
ic = -sr * shi;
|
|
d = rc*rc + ic * ic;
|
|
r.real = (rs*rc + is*ic) / d;
|
|
r.imag = (is*rc - rs*ic) / d;
|
|
return r;
|
|
}
|
|
|
|
static char c_tan_doc[] =
|
|
"tan(x)\n"
|
|
"\n"
|
|
"Return the tangent of x.";
|
|
|
|
|
|
static Py_complex
|
|
c_tanh(Py_complex x)
|
|
{
|
|
Py_complex r;
|
|
double si,ci,shr,chr;
|
|
double rs,is,rc,ic;
|
|
double d;
|
|
si = sin(x.imag);
|
|
ci = cos(x.imag);
|
|
shr = sinh(x.real);
|
|
chr = cosh(x.real);
|
|
rs = ci * shr;
|
|
is = si * chr;
|
|
rc = ci * chr;
|
|
ic = si * shr;
|
|
d = rc*rc + ic*ic;
|
|
r.real = (rs*rc + is*ic) / d;
|
|
r.imag = (is*rc - rs*ic) / d;
|
|
return r;
|
|
}
|
|
|
|
static char c_tanh_doc[] =
|
|
"tanh(x)\n"
|
|
"\n"
|
|
"Return the hyperbolic tangent of x.";
|
|
|
|
|
|
/* And now the glue to make them available from Python: */
|
|
|
|
static PyObject *
|
|
math_error(void)
|
|
{
|
|
if (errno == EDOM)
|
|
PyErr_SetString(PyExc_ValueError, "math domain error");
|
|
else if (errno == ERANGE)
|
|
PyErr_SetString(PyExc_OverflowError, "math range error");
|
|
else /* Unexpected math error */
|
|
PyErr_SetFromErrno(PyExc_ValueError);
|
|
return NULL;
|
|
}
|
|
|
|
static PyObject *
|
|
math_1(PyObject *args, Py_complex (*func)(Py_complex))
|
|
{
|
|
Py_complex x;
|
|
if (!PyArg_ParseTuple(args, "D", &x))
|
|
return NULL;
|
|
errno = 0;
|
|
PyFPE_START_PROTECT("complex function", return 0)
|
|
x = (*func)(x);
|
|
PyFPE_END_PROTECT(x)
|
|
CHECK(x.real);
|
|
CHECK(x.imag);
|
|
if (errno != 0)
|
|
return math_error();
|
|
else
|
|
return PyComplex_FromCComplex(x);
|
|
}
|
|
|
|
#define FUNC1(stubname, func) \
|
|
static PyObject * stubname(PyObject *self, PyObject *args) { \
|
|
return math_1(args, func); \
|
|
}
|
|
|
|
FUNC1(cmath_acos, c_acos)
|
|
FUNC1(cmath_acosh, c_acosh)
|
|
FUNC1(cmath_asin, c_asin)
|
|
FUNC1(cmath_asinh, c_asinh)
|
|
FUNC1(cmath_atan, c_atan)
|
|
FUNC1(cmath_atanh, c_atanh)
|
|
FUNC1(cmath_cos, c_cos)
|
|
FUNC1(cmath_cosh, c_cosh)
|
|
FUNC1(cmath_exp, c_exp)
|
|
FUNC1(cmath_log, c_log)
|
|
FUNC1(cmath_log10, c_log10)
|
|
FUNC1(cmath_sin, c_sin)
|
|
FUNC1(cmath_sinh, c_sinh)
|
|
FUNC1(cmath_sqrt, c_sqrt)
|
|
FUNC1(cmath_tan, c_tan)
|
|
FUNC1(cmath_tanh, c_tanh)
|
|
|
|
|
|
static char module_doc[] =
|
|
"This module is always available. It provides access to mathematical\n"
|
|
"functions for complex numbers.";
|
|
|
|
static PyMethodDef cmath_methods[] = {
|
|
{"acos", cmath_acos, METH_VARARGS, c_acos_doc},
|
|
{"acosh", cmath_acosh, METH_VARARGS, c_acosh_doc},
|
|
{"asin", cmath_asin, METH_VARARGS, c_asin_doc},
|
|
{"asinh", cmath_asinh, METH_VARARGS, c_asinh_doc},
|
|
{"atan", cmath_atan, METH_VARARGS, c_atan_doc},
|
|
{"atanh", cmath_atanh, METH_VARARGS, c_atanh_doc},
|
|
{"cos", cmath_cos, METH_VARARGS, c_cos_doc},
|
|
{"cosh", cmath_cosh, METH_VARARGS, c_cosh_doc},
|
|
{"exp", cmath_exp, METH_VARARGS, c_exp_doc},
|
|
{"log", cmath_log, METH_VARARGS, c_log_doc},
|
|
{"log10", cmath_log10, METH_VARARGS, c_log10_doc},
|
|
{"sin", cmath_sin, METH_VARARGS, c_sin_doc},
|
|
{"sinh", cmath_sinh, METH_VARARGS, c_sinh_doc},
|
|
{"sqrt", cmath_sqrt, METH_VARARGS, c_sqrt_doc},
|
|
{"tan", cmath_tan, METH_VARARGS, c_tan_doc},
|
|
{"tanh", cmath_tanh, METH_VARARGS, c_tanh_doc},
|
|
{NULL, NULL} /* sentinel */
|
|
};
|
|
|
|
DL_EXPORT(void)
|
|
initcmath(void)
|
|
{
|
|
PyObject *m, *d, *v;
|
|
|
|
m = Py_InitModule3("cmath", cmath_methods, module_doc);
|
|
d = PyModule_GetDict(m);
|
|
PyDict_SetItemString(d, "pi",
|
|
v = PyFloat_FromDouble(atan(1.0) * 4.0));
|
|
Py_DECREF(v);
|
|
PyDict_SetItemString(d, "e", v = PyFloat_FromDouble(exp(1.0)));
|
|
Py_DECREF(v);
|
|
}
|