This page is a snapshot from the LWG issues list, see the Library Active Issues List for more information and the meaning of C++17 status.
std::abs(short)
, std::abs(signed char)
and others should return int
instead of
double
in order to be compatible with C++98 and CSection: 29.7 [c.math] Status: C++17 Submitter: Jörn Heusipp Opened: 2016-06-16 Last modified: 2017-07-30
Priority: 3
View all other issues in [c.math].
View all issues with C++17 status.
Discussion:
Consider this C++98 program:
#include <cmath> #include <cstdlib> int main() { return std::abs(static_cast<short>(23)) % 42; }
This works fine with C++98 compilers. At the std::abs(short)
call, short gets promoted to int
and
std::abs(int)
is called.
Otherwise, if any argument of arithmetic type corresponding to a
double
parameter has typedouble
or an integer type, then all arguments of arithmetic type corresponding todouble
parameters are effectively cast todouble
.
C++17 draft additionally adds on page 1080 §26.9 p10 [c.math]:
If
abs()
is called with an argument of typeX
for whichis_unsigned<X>::value
istrue
and ifX
cannot be converted toint
by integral promotion (4.5), the program is ill-formed. [Note: Arguments that can be promoted toint
are permitted for compatibility with C. — end note]
It is somewhat confusing and probably even contradictory to on the one hand specify abs()
in terms of integral
promotion in §26.9 p10 and on the other hand demand all integral types to be converted to double
in
§26.9 p15 b2.
std::abs
and compile the code successfully. GCC 4.5-5.3 (for std::abs
but
not for ::abs
) as well as GCC >=6.0 (for both std::abs
and ::abs
) fail to compile in the following
way: Taking §26.9 p15 b2 literally and applying it to abs()
(which is listed in §26.9 p12) results in
abs(short)
returning double
, and with operator%
not being specified for double
, this
makes the programm ill-formed.
I do acknowledge the reason for the wording and semantics demanded by §26.9 p15 b2, i.e. being able to call math functions
with integral types or with partly floating point types and partly integral types. Converting integral types to double
certainly makes sense here for all the other floating point math functions.
However, abs()
is special. abs()
has overloads for the 3 wider integral types which return integral types.
abs()
originates in the C standard in stdlib.h
and had originally been specified for integral types only.
Calling it in C with a short argument returns an int
. Calling std::abs(short)
in C++98 also returns an
int
. Calling std::abs(short)
in C++11 and later with §26.9 p15 b2 applied to abs()
suddenly
returns a double
.
Additionally, this behaviour also breaks third-party C headers which contain macros or inline functions calling
abs(short)
.
As per discussion on std-discussion, my reading of the standard as well as GCC's interpretation seem valid.
However, as can be seen, this breaks existing code.
In addition to the compatibilty concerns, having std::abs(short)
return double
is also very confusing
and unintuitive.
The other (possibly, depending on their respective size relative to int
) affected types besides short
are signed char
, unsigned char
and unsigned short
, and also char
, char16_t
,
char32_t
and wchar_t
, (all of these are or may be promotable to int
). Wider integral types
are not affected because explicit overloads are specified for those types by §26.9 p6, §26.9 p7 and §26.9 p9.
div()
is also not affected because it is neither listed in §26.9 p12, nor does it actually provide
any overload for double
at all.
As far as I can see, the proposed or implemented solutions for LWG 2294(i), 2192(i) and/or
2086(i) do not resolve this issue.
I think both, §26.9 p10 [c.math] and §26.9 p15 [c.math] need some correction and clarification.
(Note: These changes would explicitly render the current implementation in GCC's libstdc++ non-conforming, which would
be a good thing, as outlined above.)
Previous resolution [SUPERSEDED]:
This wording is relative to N4594.
Modify 29.7 [c.math] as indicated:
-10- If
[…] -15- Moreover, there shall be additional overloads for these functions, with the exception ofabs()
is called with an argument of typeX
for whichis_unsigned<X>::value
istrue
and ifX
cannot be converted toint
by integral promotion (4.5), the program is ill-formed. Ifabs()
is called with an argument of typeX
which can be converted toint
by integral promotion (4.5), the argument is promoted toint
. [Note: Arguments that can be promoted toint
are promoted toint
in order to keeppermitted forcompatibility with C. — end note]abs()
, sufficient to ensure:
If any argument of arithmetic type corresponding to a
double
parameter has typelong double
, then all arguments of arithmetic type (3.9.1) corresponding todouble
parameters are effectively cast tolong double
.Otherwise, if any argument of arithmetic type corresponding to a
double
parameter has typedouble
or an integer type, then all arguments of arithmetic type corresponding todouble
parameters are effectively cast todouble
.Otherwise, all arguments of arithmetic type corresponding to
double
parameters have typefloat
.See also: ISO C 7.5, 7.10.2, 7.10.6.
[Note:abs()
is exempted from these rules in order to stay compatible with C. — end note]
[2016-07 Chicago]
Monday: Some of this has been changed in N4606; the rest of the changes may be editorial.
Fri PM: Move to Tentatively Ready
Proposed resolution:
This wording is relative to N4606.
Modify 29.7.1 [cmath.syn] as indicated:
-2- For each set of overloaded functions within
<cmath>
, with the exception ofabs
, there shall be additional overloads sufficient to ensure:
If any argument of arithmetic type corresponding to a
double
parameter has typelong double
, then all arguments of arithmetic type (3.9.1) corresponding todouble
parameters are effectively cast tolong double
.Otherwise, if any argument of arithmetic type corresponding to a
double
parameter has typedouble
or an integer type, then all arguments of arithmetic type corresponding todouble
parameters are effectively cast todouble
.Otherwise, all arguments of arithmetic type corresponding to
double
parameters have typefloat
.[Note:
abs
is exempted from these rules in order to stay compatible with C. — end note]See also: ISO C 7.5, 7.10.2, 7.10.6.