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.

### 2735. `std::abs(short)`, `std::abs(signed char)` and others should return `int` instead of `double` in order to be compatible with C++98 and C

Section: 28.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.

C++11 added the following wording on page 1083 §26.9 p15 b2 [c.math]:

> Otherwise, if any argument of arithmetic type corresponding to a `double` parameter has type `double` or an integer type, then all arguments of arithmetic type corresponding to `double` parameters are effectively cast to `double`.

C++17 draft additionally adds on page 1080 §26.9 p10 [c.math]:

> If `abs()` is called with an argument of type `X` for which `is_unsigned<X>::value` is `true` and if `X` cannot be converted to `int` by integral promotion (4.5), the program is ill-formed. [Note: Arguments that can be promoted to `int` 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.

Most compilers (each with their own respective library implementation) I tested (MSVC, Clang, older GCC) appear to not consider §26.9 p15 b2 for `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, 2192 and/or 2086 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.

> 1. Modify 28.7 [c.math] as indicated:

>    > -10- If `abs()` is called with an argument of type `X` for which `is_unsigned<X>::value` is `true` and if `X` cannot be converted to `int` by integral promotion (4.5), the program is ill-formed. If `abs()` is called with an argument of type `X` which can be converted to `int` by integral promotion (4.5), the argument is promoted to `int`. [Note: Arguments that can be promoted to `int` are promoted to `int` in order to keep permitted for compatibility with C. — end note]

>    > […]

>    > -15- Moreover, there shall be additional overloads for these functions, with the exception of `abs()`, sufficient to ensure:

>    > 1. If any argument of arithmetic type corresponding to a `double` parameter has type `long double`, then all arguments of arithmetic type (3.9.1) corresponding to `double` parameters are effectively cast to `long double`.

>    > 2. Otherwise, if any argument of arithmetic type corresponding to a `double` parameter has type `double` or an integer type, then all arguments of arithmetic type corresponding to `double` parameters are effectively cast to `double`.

>    > 3. Otherwise, all arguments of arithmetic type corresponding to `double` parameters have type `float`.

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

1. Modify 28.7.1 [cmath.syn] as indicated:

   > -2- For each set of overloaded functions within `<cmath>`, with the exception of `abs`, there shall be additional overloads sufficient to ensure:

   > 1. If any argument of arithmetic type corresponding to a `double` parameter has type `long double`, then all arguments of arithmetic type (3.9.1) corresponding to `double` parameters are effectively cast to `long double`.

   > 2. Otherwise, if any argument of arithmetic type corresponding to a `double` parameter has type `double` or an integer type, then all arguments of arithmetic type corresponding to `double` parameters are effectively cast to `double`.

   > 3. Otherwise, all arguments of arithmetic type corresponding to `double` parameters have type `float`.

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