Section: 29.7 [c.math] Status: C++14 Submitter: Daniel Krügler Opened: 2011-09-22 Last modified: 2016-01-28
Priority: Not Prioritized
View all other issues in [c.math].
View all issues with C++14 status.
Discussion:
29.7 [c.math] ends with a description of a rule set for "sufficient overloads" in p11:
Moreover, there shall be additional overloads sufficient to ensure:
- If any argument corresponding to a
double
parameter has typelong double
, then all arguments corresponding todouble
parameters are effectively cast tolong double
.- Otherwise, if any argument corresponding to a
double
parameter has typedouble
or an integer type, then all arguments corresponding todouble
parameters are effectively cast todouble
.- Otherwise, all arguments corresponding to
double
parameters are effectively cast tofloat
.
My impression is that this rule set is probably more generic as intended, my assumption is that it is written to mimic the C99/C1x rule set in 7.25 p2+3 in the "C++" way:
-2- Of the
-3- Use of the macro invokes a function whose generic parameters have the corresponding real type determined as follows:<math.h>
and<complex.h>
functions without anf
(float
) orl
(long double
) suffix, several have one or more parameters whose corresponding real type isdouble
. For each such function, exceptmodf
, there is a corresponding type-generic macro. (footnote 313) The parameters whose corresponding real type isdouble
in the function synopsis are generic parameters. Use of the macro invokes a function whose corresponding real type and type domain are determined by the arguments for the generic parameters. (footnote 314)
- First, if any argument for generic parameters has type
long double
, the type determined islong double
.- Otherwise, if any argument for generic parameters has type
double
or is of integer type, the type determined isdouble
.- Otherwise, the type determined is
float
.
where footnote 314 clarifies the intent:
If the type of the argument is not compatible with the type of the parameter for the selected function, the behavior is undefined.
The combination of the usage of the unspecific term "cast" with otherwise no further constraints (note that C constraints the valid set to types that C++ describes as arithmetic types, but see below for one important difference) has the effect that it requires the following examples to be well-formed and well-defined:
#include <cmath> enum class Ec { }; struct S { explicit operator long double(); }; void test(Ec e, S s) { std::sqrt(e); // OK, behaves like std::sqrt((float) e); std::sqrt(s); // OK, behaves like std::sqrt((float) s); }
GCC 4.7 does not accept any of these examples.
I found another example where the C++ rule differs from the C set, but in this case I'm not so sure, which direction C++ should follow. The difference is located in the fact, that in C enumerated types are integer types as described in 6.2.5 p17 (see e.g. n1569 or n1256): "The type char, the signed and unsigned integer types, and the enumerated types are collectively called integer types. The integer and real floating types are collectively called real types." This indicates that in C the following code#include <math.h> enum E { e }; void test(void) { sqrt(e); // OK, behaves like sqrt((double) e); }
seems to be well-defined and e
is cast to double
, but in C++
referring to
#include <cmath> enum E { e }; void test() { std::sqrt(e); // OK, behaves like sqrt((float) e); }
is also well-defined (because of our lack of constraints) but we
must skip bullet 2 (because E is not an integer type) and effectively
cast e
to float
. Accepting this, we would introduce
a silent, but observable runtime difference for C and C++.
Howard provided wording to solve the issue.
[2012, Kona]
Moved to Ready. The proposed wording reflects both original intent from TR1, and current implementations.
[2012, Portland: applied to WP]
Proposed resolution:
This wording is relative to the FDIS.
Change 29.7 [c.math] p11 as indicated:
Moreover, there shall be additional overloads sufficient to ensure:
- If any arithmetic argument corresponding to a
double
parameter has typelong double
, then all arithmetic arguments corresponding todouble
parameters are effectively cast tolong double
.- Otherwise, if any arithmetic argument corresponding to a
double
parameter has typedouble
or an integer type, then all arithmetic arguments corresponding todouble
parameters are effectively cast todouble
.- Otherwise, all arithmetic arguments corresponding to
double
parametersare effectively cast tohave typefloat
.