JTC1/SC22/WG21
N1502
N1502=03-0085
PROPOSED SIGNATURE CHANGES FOR SPECIAL
MATH FUNCTIONS IN TR-1
P.J. Plauger
Dinkumware, Ltd.
pjp@dinkumware.com
TR-1 includes a number of special math functions described by
Walter E. Brown in WG21/N1422. Our experience so far is that
these functions are implementable, even though they are not easy
to implement well in all precisions. In fact, we are proposing
to WG14 that they be considered for addition to the next C Standard
as well. But we have problems both with the choice of names and the
order of arguments to these functions. We thus propose the following
changes.
NAMES
First consider the names. As proposed in N1422 (and modified
slightly in a revision which we have seen), the names deviate
from past practice in naming math.h functions in several ways:
-- Names are a mixture of upper and lower case, unlike
existing math functions.
-- Some names differ only in the case of the last letter,
an invitation to confusion.
-- The last letter sometimes reflects mathematical notation that
is far from universal.
Modern linkers no longer suffer from many of the constraints that
shaped earlier names of math functions, but readability is still
an issue. And compatibility with Standard C should remain an issue.
Standard C still has compelling reasons to offer three variations
on each function (despite the presence of tgmath.h in C99):
-- No suffix, for double, as in atan2.
-- Lower-case F suffix, for float, as in atan2f.
-- Lower-case L suffix, for long double, as in atan2l.
It would be nice if the f and l suffixes did not get easily
confused as part of the root name.
We thus propose the following changes. (Names in parentheses are
those already changed by Brown in his revised paper.)
Original Proposed
bessel_I cyl_bessel_i
bessel_J cyl_bessel_j
bessel_K cyl_bessel_k
bessel_j sph_bessel
beta beta
ei expint
ellint_E ellint_2
ellint_E2 comp_ellint_2
ellint_F ellint_1
ellint_K comp_ellint_1
ellint_P ellint_3
ellint_P2 comp_ellint_3
hermite hermite
hyperg_1F1 conf_hyperg
hyperg_2F1 hyperg
laguerre_0 laguerre
laguerre_m assoc_laguerre
legendre_Pl legendre
legendre_Plm assoc_legendre
neumann_N cyl_neumann
neumann_n sph_neumann
sph_Y (sph_legendre_Plm) sph_legendre
zeta (riemann_zeta) riemann_zeta
Rationale:
-- All lower-case names are the norm for functions in math.h.
-- The cylindrical Bessel functions are widely known by the
I/J/K single-letter abbreviations.
-- Any confusion between the cylindrical J Bessel and
spherical j Bessel can be mitigated by the uniform use of
cyl_ and sph_ prefixes.
-- The elliptical integrals are more widely known by their
"kind" (1st, 2nd, 3rd) than their single-letter abbreviations.
-- The hypergeometric functions are likewise known best as
either confluent or not.
-- The naming of variant Laguerre and Legendre polynomials
(assoc_ for associated) is more uniform.
-- More than one zeta function is widely used, so it doesn't
hurt to identify the Riemann zeta more precisely. (This is
much less a problem with the beta and gamma functions.)
ARGUMENT ORDER
Most of the functions in N1422 are parametric -- one or more
arguments define a whole family of related functions. These
parameters are prime candidates for having default values.
But N1422 uniformly places the independent variable (usually
written x) at the end of the argument list. No strong precedent
exists for this practice -- notation varies considerably among
both textbooks and computer functions. Making x the first
argument permits C++ to define sensible defaults for the
remaining parameters.
Thus we propose the following signatures and default values.
The handful of definitions merely serves to highlight an
obvious relationship to other functions; they are *not*
intended as required definitions, since they are often
computationally ill advised:
double cyl_bessel_i(double x, double n = 0);
double cyl_bessel_j(double x, double n = 0);
double cyl_bessel_k(double x, double n = 0);
double sph_bessel(double x, double n = 0)
{return sqrt(pi / (2 * x) * cyl_bessel(x, n + 0.5); }
double cyl_neumann(double x, double n = 0);
double sph_neumann(double x, double n = 0)
{return sqrt(pi / (2 * x) * cyl_neumann(x, n + 0.5); }
double beta(double x, double y)
{return tgamma(x) * tgamma(y) / tgamma(x + y); }
double expint(double x);
double ellint_1(double k, double phi = pi/2);
double comp_ellint_1(double k)
{return ellint_1(k, pi/2); }
double ellint_2(double k, double phi = pi/2);
double comp_ellint_2(double k)
{return ellint_2(k, pi/2); }
double ellint_3(double k, double n = 0, double phi = pi/2);
double comp_ellint_3(double k, double n = 0)
{return ellint_3(k, n, pi/2); }
double hyperg(double x, double a, double b, double c);
double conf_hyperg(double x, double a, double c);
double hermite(double x, unsigned int n = 0);
double assoc_laguerre(double x, unsigned int el = 0, unsigned int m = 0);
double laguerre(double x, unsigned int el = 0)
{return assoc_laguerre(x, el, 0); }
double assoc_legendre(double x, unsigned int el = 0, unsigned int m = 0);
double legendre(double x, unsigned int el = 0)
{return assoc_legendre(x, el); }
double sph_legendre(double theta, unsigned int el = 0, int m = 0)
{return f(el, m) * assoc_legendre(cos(theta), el, m); }
double riemann_zeta(double x);
C COMPATIBILITY
If the additions for C99 compatibility are approved for TR-1, it
would make sense to define the *f and *l versions of all these
functions in C++. And, of course, we should also add the overloads
for these functions needed to match the argument promotion rules
of the C99 generics. For example, riemann_zeta(2) should call the
double version of this function, not cause a compile-time
ambiguity.