ISO/ IEC JTC1/SC22/WG21 N1502



P.J. Plauger
Dinkumware, Ltd.

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


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


-- 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.)


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);


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