Date: Mon, 12 Mar 2018 21:04:46 +0000
On Mon, Mar 12, 2018 at 13:32 Lawrence Crowl <Lawrence_at_[hidden]> wrote:
> On 3/12/18, Myria <myriachan_at_[hidden]> wrote:
> > The severity of the current situation is that I generally avoid signed
> > integers if I intend to do any arithmetic on them whatsoever, lest the
> > compiler decide to make demons come out of my nose.
>
> So why not specify the option to turn on trapping?
>
> > And even then, I'm not safe:
> >
> > std::uint16_t x = 0xFFFF;
> > x *= x; // undefined behavior on most modern platforms
>
> How? The C++ standard defines unsigned arithmetic as
> modular arithmetic.
>
But that's the catch: it's double secret signed arithmetic. The promotion
rules of C, inherited by C++, state that on any arithmetic operation,
integer types of rank less than int promote to int. This promotion is
regardless of signedness.
On a "typical modern platform", std::uint16_t is unsigned short. That is
of lesser rank than signed int, so it promotes to signed int on any
arithmetic operation, resulting in the following:
int promoted_x = x;
x = static_cast<std::uint16_t>(promoted_x * promoted_x);
65535 * 65535 overflows a signed int on a typical 32-bit int platform,
which is undefined behavior.
> More importantly, what happens to your program when x*x < x?
>
The code that led me to finding this was a 16-bit variant of the FNV hash
function, so it worked properly after the correct casts were added to allow
the wrap.
> > My code has to do silly things like this in order to safeguard against
> > such potential compiler abuses:
> >
> > typedef decltype(std::uint16_t() + 0u) promoted_uint16;
>
> How does this typedef help?
Arithmetic between any unsigned type and unsigned int results in a type of
at least the first type's size that cannot be promoted to a signed type in
arithmetic with other unsigned types.
It's like uint16_fast_t, except that it guarantees that all operations
performed will be well-defined to wrap, with just the inconvenience of
potentially being larger than the actual intended type.
> > I would be happy if an option like -fwrapv were supported everywhere,
> > but Visual Studio doesn't have such an option, and Microsoft has
> > already denied requests for such an option to be implemented.
>
> What about -ftrapv?
>
If I were working on something where signed int overflow were a problem,
then sure, in debug builds. In release builds, I wouldn't use that for
performance reasons (except where it's mostly free, like on MIPS).
Melissa
> On 3/12/18, Myria <myriachan_at_[hidden]> wrote:
> > The severity of the current situation is that I generally avoid signed
> > integers if I intend to do any arithmetic on them whatsoever, lest the
> > compiler decide to make demons come out of my nose.
>
> So why not specify the option to turn on trapping?
>
> > And even then, I'm not safe:
> >
> > std::uint16_t x = 0xFFFF;
> > x *= x; // undefined behavior on most modern platforms
>
> How? The C++ standard defines unsigned arithmetic as
> modular arithmetic.
>
But that's the catch: it's double secret signed arithmetic. The promotion
rules of C, inherited by C++, state that on any arithmetic operation,
integer types of rank less than int promote to int. This promotion is
regardless of signedness.
On a "typical modern platform", std::uint16_t is unsigned short. That is
of lesser rank than signed int, so it promotes to signed int on any
arithmetic operation, resulting in the following:
int promoted_x = x;
x = static_cast<std::uint16_t>(promoted_x * promoted_x);
65535 * 65535 overflows a signed int on a typical 32-bit int platform,
which is undefined behavior.
> More importantly, what happens to your program when x*x < x?
>
The code that led me to finding this was a 16-bit variant of the FNV hash
function, so it worked properly after the correct casts were added to allow
the wrap.
> > My code has to do silly things like this in order to safeguard against
> > such potential compiler abuses:
> >
> > typedef decltype(std::uint16_t() + 0u) promoted_uint16;
>
> How does this typedef help?
Arithmetic between any unsigned type and unsigned int results in a type of
at least the first type's size that cannot be promoted to a signed type in
arithmetic with other unsigned types.
It's like uint16_fast_t, except that it guarantees that all operations
performed will be well-defined to wrap, with just the inconvenience of
potentially being larger than the actual intended type.
> > I would be happy if an option like -fwrapv were supported everywhere,
> > but Visual Studio doesn't have such an option, and Microsoft has
> > already denied requests for such an option to be implemented.
>
> What about -ftrapv?
>
If I were working on something where signed int overflow were a problem,
then sure, in debug builds. In release builds, I wouldn't use that for
performance reasons (except where it's mostly free, like on MIPS).
Melissa
Received on 2018-03-12 22:04:58