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.