Unexpected integral promotions

127 views
Skip to first unread message

d.n.i...@gmail.com

unread,
Feb 1, 2016, 3:38:20 AM2/1/16
to ISO C++ Standard - Discussion

Compiling this code (http://goo.gl/aKLau6):


#include <cstdint>
#include <type_traits>

int main ()
{
    auto x = std::uint8_t{5};
    x += std::uint8_t{1};
    static_assert(std::is_same<decltype(x += std::uint8_t{1}), std::uint8_t &>::value, "");

    auto y = std::int16_t{6};
    y <<= std::int16_t{2};
    static_assert(std::is_same<decltype(y <<= std::int16_t{2}), std::int16_t &>::value, "");
}


we receive compiler warnings:

$ g++-5 -Wconversion -std=c++14 main.cpp

main.cpp: In function ‘int main()’:

main.cpp:6:7: warning: conversion to ‘unsigned char’ from ‘int’ may alter its value [-Wconversion]

     x += std::uint8_t{1};

       ^

main.cpp:9:7: warning: conversion to ‘short int’ from ‘int’ may alter its value [-Wconversion]

     y <<= std::int16_t{2};

       ^


We see that some integral promotions to variables 'x' and 'y' are applied. But it doesn't make any sense because we are just changing the value. Types of 'x' and 'y', of course, would not change after the respective operations. Moreover, the type of the whole expression `x += std::uint8_t{1}` is `std::uint8_t &`, and `std::int16_t &` for expression `y <<= std::int16_t{2}`, just as expected. So why should we be aware of the implicit roundtrip conversion somewhere in the deep implementation?

The C++ Standard says that everything is OK, but in my opinion in this particular case it is senseless, causes some difficulties and results bad code with lots of redundant explicit casts.

Edward Catmur

unread,
Feb 1, 2016, 6:04:11 AM2/1/16
to ISO C++ Standard - Discussion
Promotion of arithmetic operands accurately reflects reality on platforms that do not have arithmetic instructions for sub-int types. Because of this and also for compatibility with code that depends on the promotions, there is zero prospect of changing this.

Note that even for arithmetic assignment operations the behavior is different from what it would be if the operation were carried out in the operand type, as signed overflow is implementation-defined when the intermediate result is representable in int but not in the operand type; it is only undefined when overflow occurs in int.

The compiler warnings do appear redundant in your case, but compiler warnings are not specified by the standard. Have you contacted your compiler vendor to request that they suppress the warning in the case of arithmetic assignment operations?

Myriachan

unread,
Feb 3, 2016, 2:58:18 PM2/3/16
to ISO C++ Standard - Discussion

The worst part of promotion is that unsigned types become signed, resulting in totally-unexpected things like this being undefined behavior on platforms with a 32-bit int:

std::uint16_t x = 0xFFFF;
x
*= x;

If it weren't for promotion from unsigned short to signed int, this would be defined behavior.

This kind of crap is making my current project really difficult, because I have to constantly explicitly promote with "0u +" everywhere.

Melissa

Jens Maurer

unread,
Feb 3, 2016, 5:01:46 PM2/3/16
to std-dis...@isocpp.org
On 02/03/2016 08:58 PM, Myriachan wrote:

> std::uint16_t x =0xFFFF;
> x *=x;
>
> If it weren't for promotion from unsigned short to signed int, this would be defined behavior.
>
> This kind of crap is making my current project really difficult, because I have to constantly explicitly promote with "0u +" everywhere.

This sounds error-prone.

Have you tried using a home-grown class with the arithmetic operations as
you want them instead of std::uint16_t?

Jens

Jacob McIntosh (nacitar sevaht)

unread,
Feb 3, 2016, 5:38:51 PM2/3/16
to std-dis...@isocpp.org

The thing with unsigned types being promoted to signed ones has always been a point one ends up having to address when debating others about the design of c++ versus other languages... and it's pretty indefensible.  Is there _anything_ good about this silly "feature" rather than promoting to an unsigned int instead?


--

---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussio...@isocpp.org.
To post to this group, send email to std-dis...@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.

Nicol Bolas

unread,
Feb 3, 2016, 11:52:29 PM2/3/16
to ISO C++ Standard - Discussion
On Wednesday, February 3, 2016 at 5:38:51 PM UTC-5, Jacob McIntosh wrote:

The thing with unsigned types being promoted to signed ones has always been a point one ends up having to address when debating others about the design of c++ versus other languages... and it's pretty indefensible.  Is there _anything_ good about this silly "feature" rather than promoting to an unsigned int instead?


If you can find a way to change it without breaking the world, I'd love to hear it. Otherwise, good or bad, it's what we've got.

Myriachan

unread,
Feb 4, 2016, 3:13:51 PM2/4/16
to ISO C++ Standard - Discussion

Even though I dislike the status quo, changing the promotion rules now would be crazy.  I'd rather just have the rules changed to make signed overflow implementation-defined as wrap, snap or trap, similar to the situation with signed shift right of negative numbers.

Melissa

Thiago Macieira

unread,
Feb 4, 2016, 5:12:54 PM2/4/16
to std-dis...@isocpp.org
On quinta-feira, 4 de fevereiro de 2016 12:13:50 PST Myriachan wrote:
> Even though I dislike the status quo, changing the promotion rules now
> would be crazy. I'd rather just have the rules changed to make signed
> overflow implementation-defined as wrap, snap or trap, similar to the
> situation with signed shift right of negative numbers.

Any one of those choices would imply a cost for some machines, plus removing
the ability to optimise certain code today.

The best way to achieve that behaviour is to have a library that implements
wrapping, saturating and trapping properly, on command.

--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center

Edward Catmur

unread,
Feb 5, 2016, 4:18:29 AM2/5/16
to std-dis...@isocpp.org

On Thu, Feb 4, 2016 at 4:52 AM, Nicol Bolas <jmck...@gmail.com> wrote:
>
> If you can find a way to change it without breaking the world, I'd love to hear it. Otherwise, good or bad, it's what we've got.

It might be possible to track "promotedness" through an expression and intervene to prevent UB occurring.

Here's a sketch:
1. Introduce a new expression category, *unsigned-promoted-to-signed expression*.
2. When a prvalue of unsigned integral type is promoted to a signed type, the result is an unsigned-promoted-to-signed expression.
3. When the usual arithmetic conversions are applied to the operands of an arithmetic operation or to the second and third operands of a ternary conditional expression, if one operand is an unsigned-promoted-to-signed expression of the same type as the result type and the other operand either a) is an unsigned-promoted-to-signed expression, b) has type of lesser rank than the result type, or c) is a constant expression having value representable in the unsigned type corresponding to the result type, the result is an unsigned-promoted-to-signed expression.
4. If the operand to a unary arithmetic operation or the left operand to an arithmetic shift operation is an unsigned-promoted-to-signed expression, the result is an unsigned-promoted-to-signed expression.
5. If the operand to a parenthesized expression or the right operand to a comma expression is an unsigned-promoted-to-signed expression, the result is an unsigned-promoted-to-signed expression.
6. When the result of an arithmetic operation is not representable in the result type, if the result is an unsigned-promoted-to-signed expression, the result of the operation is first converted to the unsigned type corresponding to the result type according to the rules of 2^n modulo arithmetic, and from there converted to the result type according to the rules of arithmetic conversion. [Note: - the result may be implementation-defined if the value in the corresponding unsigned type is not representable in the result type, but is never undefined. - end note]

I don't think it quite works for shift operations, but I hope you can see the sort of thing that would be required. The question is whether the additional ugliness and confusion would be worth it; or whether the effort of confirming that a promoted expression is covered by those rules is less than the effort of simply inserting casts to unsigned int.

Reply all
Reply to author
Forward
0 new messages