3

Functions g1() and g2() have identical logic, but the input types have different sizes. Why do they return different results for negative sums?

/*BINFMTCXX: -Wall -Werror -Wextra -std=c++11
*/

#include <stdio.h>
#include <stdint.h>

char g1( int32_t a, uint32_t b ) { return a+b<9; } // fails when a+b is negative
char g2( int16_t a, uint16_t b ) { return a+b<9; } // works if no overflow

int main()
   {

   for ( int a=-2, b=0; a<=2; a++ )
      {
      fprintf(stderr,"a=%+d, b=%d, g1=%+d, g2=%+d %s\n", a, b, g1(a,b), g2(a,b), g1(a,b)==g2(a,b)?"":"!" );
      }

   return 0;
   }

When I run it, it shows that g1() fails when a+b is negative:

$ ./mixed_sign_math_per_size.cpp
a=-2, b=0, g1=+0, g2=+1 !
a=-1, b=0, g1=+0, g2=+1 !
a=+0, b=0, g1=+1, g2=+1 
a=+1, b=0, g1=+1, g2=+1 
a=+2, b=0, g1=+1, g2=+1 

The results are the same in C and C++.

Brent Bradburn
  • 51,587
  • 17
  • 154
  • 173
  • *but the input types have different sizes.* -- Don't you think what is more important is that the types are signed / unsigned rather than the size of these types? – PaulMcKenzie Aug 09 '16 at 22:34
  • @PaulMcKenzie: Yes, the input types have mixed sign. However, this is not a difference between `g1()` and `g2()`. – Brent Bradburn Aug 09 '16 at 22:38
  • 1
    See http://stackoverflow.com/questions/2280663/in-a-c-expression-where-unsigned-int-and-signed-int-are-present-which-type-will to explain `g1`, the negative number is converted to a very large unsigned number. For `g2`, both are converted to `int` which has enough range to handle both inputs without loss. – Mark Ransom Aug 09 '16 at 22:41
  • 1
    I don't know why but: in g1() `decltype(a+b)` is unsigned; in g2() `decltype(a+b)` is signed. Try `decltype(a+b) r = a+b; return r<9;` and debug it. – Richard Critten Aug 09 '16 at 22:43
  • 1
    FWIW: The code in the dup question would likely trigger compiler warnings, whereas my code did not (in either C or C++). Nevertheless, the answers are closely related -- even though that question does not specify C++. – Brent Bradburn Aug 10 '16 at 00:03

1 Answers1

3

As a result of the usual arithmetic conversions, both a and b in g2's body are promoted to int, which is why that function works perfectly well.

For g1, because (u)int32_t does not have a rank less than that of int, no promotion occurs, and the very last bullet point (11.5.5) applies. Both operands are converted to the unsigned type, which - in a's case - causes an underflow, producing a value much greater than 9. Hence g1 returned 1 (true).

Columbo
  • 60,038
  • 8
  • 155
  • 203
  • I can't figure out what you are referencing as "10.5.5". Your link was to 5.11. – Brent Bradburn Aug 09 '16 at 23:22
  • @nobar My bad, I was looking at a local, outdated draft. (11.5.5) is the fifth bullet point of the fifth bullet point in paragraph 11. – Columbo Aug 10 '16 at 00:23
  • This is IMO a bit unclear: `a` and `b` are not converted due to the "usual arithmetic conversion" but due to "integer promotion" (which happens first). At least that'd be my understanding of the exact wording in the standard. – Daniel Jour Aug 10 '16 at 15:23
  • 1
    @DanielJour My interpretation is that promotion is only applied because the fifth top-level bullet point applies. – Columbo Aug 10 '16 at 16:35
  • @Columbo Yes, that makes more sense than my interpretation. – Daniel Jour Aug 10 '16 at 20:01