title: Would it break the language or existing code if we'd add safe signed/unsigned compares to C/C++?
body: <p>After reading this question on signed/unsigned compares (they come up every couple of days I'd say): </p>
  
  <ul>
  <li><a href="https://stackoverflow.com/questions/3475841/signed-unsigned-comparison-and-wall">Signed / unsigned comparison and -Wall</a></li>
  </ul>
  
  <p>I wondered why we don't have proper signed unsigned compares and instead this horrible mess? Take the output from this small program: </p>
  
  <pre><code>#include &lt;stdio.h&gt;
  #define C(T1,T2)\
   {signed   T1 a=-1;\
   unsigned T2 b=1;\
    printf("(signed %5s)%d &lt; (unsigned %5s)%d = %d\n",#T1,(int)a,#T2,(int)b,(a&lt;b));}\
  
   #define C1(T) printf("%s:%d\n",#T,(int)sizeof(T)); C(T,char);C(T,short);C(T,int);C(T,long);
  int main()
  {
   C1(char); C1(short); C1(int); C1(long); 
  }
  </code></pre>
  
  <p>Compiled with my standard compiler (gcc, 64bit), I get this:</p>
  
  <pre><code>char:1
  (signed  char)-1 &lt; (unsigned  char)1 = 1
  (signed  char)-1 &lt; (unsigned short)1 = 1
  (signed  char)-1 &lt; (unsigned   int)1 = 0
  (signed  char)-1 &lt; (unsigned  long)1 = 0
  short:2
  (signed short)-1 &lt; (unsigned  char)1 = 1
  (signed short)-1 &lt; (unsigned short)1 = 1
  (signed short)-1 &lt; (unsigned   int)1 = 0
  (signed short)-1 &lt; (unsigned  long)1 = 0
  int:4
  (signed   int)-1 &lt; (unsigned  char)1 = 1
  (signed   int)-1 &lt; (unsigned short)1 = 1
  (signed   int)-1 &lt; (unsigned   int)1 = 0
  (signed   int)-1 &lt; (unsigned  long)1 = 0
  long:8
  (signed  long)-1 &lt; (unsigned  char)1 = 1
  (signed  long)-1 &lt; (unsigned short)1 = 1
  (signed  long)-1 &lt; (unsigned   int)1 = 1
  (signed  long)-1 &lt; (unsigned  long)1 = 0
  </code></pre>
  
  <p>If I compile for 32 bit, the result is the same except that: </p>
  
  <pre><code>long:4
  (signed  long)-1 &lt; (unsigned   int)1 = 0
  </code></pre>
  
  <p>The "How?" of all this is easy to find: Just goto section 6.3 of the C99 standard or chapter 4 of C++ and dig up the clauses which describe how the operands are converted to a common type and this can break if the common type reinterprets negative values.</p>
  
  <p>But what about the "Why?". As we can see, the '&lt;' fails in 50% of all cases, also it depends on the concrete sizes of the types, so it is platform dependent. Here are some points to consider:</p>
  
  <ul>
  <li><p>The convert &amp; compare process is not really a prime example for the Rule of Least Surprise</p></li>
  <li><p>I don't believe that there is code out there, which relies on the proposition that <code>(short)-1 &gt; (unsigned)1</code> and is <em>not</em> written by terrorists.</p></li>
  <li><p>This is all terrible when you're in C++ with template code, because you need type trait magic to knit a correct "&lt;".</p></li>
  </ul>
  
  <hr>
  
  <p>After all, comparing signed and unsigned value of different types <em>is</em> easy to implement: </p>
  
  <pre><code>signed X &lt; unsigned Y -&gt; (a&lt;(X)0) || ((Z)a&lt;(Z)b) where Z=X|Y 
  </code></pre>
  
  <p>The pre-check is cheap and can also be optimized away by the compiler if a>=0 can statically be proven. </p>
  
  <p>So here's my question: </p>
  
  <p><strong>Would it break the language or existing code if we'd add safe signed/unsigned compares to C/C++?</strong></p>
  
  <p>("Would it break the language" means would we need to make massive changes to different parts of the language to accommodate this change)</p>
  
  <hr>
  
  <p><strong>UPDATE:</strong>
  I've ran this on my good old Turbo-C++ 3.0 and got this output: </p>
  
  <pre><code>char:1
  (signed  char)-1 &lt; (unsigned  char)1 = 0
  </code></pre>
  
  <p>Why is <code>(signed char)-1 &lt; (unsigned char) == 0</code> here?</p>