<div><br><div class="gmail_quote"><div dir="auto">On Mon, Mar 12, 2018 at 13:32 Lawrence Crowl &lt;<a href="mailto:Lawrence@crowl.org">Lawrence@crowl.org</a>&gt; wrote:<br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">On 3/12/18, Myria &lt;<a href="mailto:myriachan@gmail.com" target="_blank">myriachan@gmail.com</a>&gt; wrote:<br>
&gt; The severity of the current situation is that I generally avoid signed<br>
&gt; integers if I intend to do any arithmetic on them whatsoever, lest the<br>
&gt; compiler decide to make demons come out of my nose.<br>
<br>
So why not specify the option to turn on trapping?<br>
<br>
&gt; And even then, I&#39;m not safe:<br>
&gt;<br>
&gt; std::uint16_t x = 0xFFFF;<br>
&gt; x *= x;  // undefined behavior on most modern platforms<br>
<br>
How?  The C++ standard defines unsigned arithmetic as<br>
modular arithmetic.<br>
</blockquote><div dir="auto"><br></div><div dir="auto">But that&#39;s the catch: it&#39;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.</div><div dir="auto"><br></div><div dir="auto">On a &quot;typical modern platform&quot;, 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:</div><div dir="auto"><br></div><div dir="auto">int promoted_x = x;</div><div dir="auto">x = static_cast&lt;std::uint16_t&gt;(promoted_x * promoted_x);</div><div dir="auto"><br></div><div dir="auto">65535 * 65535 overflows a signed int on a typical 32-bit int platform, which is undefined behavior.</div><div dir="auto"><br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><br>
More importantly, what happens to your program when x*x &lt; x?<br>
</blockquote><div dir="auto"><br></div><div dir="auto">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.</div><div dir="auto"><br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><br>&gt; My code has to do silly things like this in order to safeguard against<br>
&gt; such potential compiler abuses:<br>
&gt;<br>
&gt; typedef decltype(std::uint16_t() + 0u) promoted_uint16;<br>
<br>
How does this typedef help?</blockquote><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"></blockquote><div dir="auto"><br></div><div dir="auto">Arithmetic between any unsigned type and unsigned int results in a type of at least the first type&#39;s size that cannot be promoted to a signed type in arithmetic with other unsigned types.</div><div dir="auto"><br></div><div dir="auto">It&#39;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.</div><div dir="auto"><br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><br>
&gt; I would be happy if an option like -fwrapv were supported everywhere,<br>
&gt; but Visual Studio doesn&#39;t have such an option, and Microsoft has<br>
&gt; already denied requests for such an option to be implemented.<br>
<br>
What about -ftrapv?<br></blockquote><div dir="auto"><br></div><div dir="auto">If I were working on something where signed int overflow were a problem, then sure, in debug builds.  In release builds, I wouldn&#39;t use that for performance reasons (except where it&#39;s mostly free, like on MIPS).</div><div dir="auto"><br></div><div dir="auto">Melissa</div></div></div>