1

As we know, int16_t has a max value 32767, so the following code will just loop:

for (int16_t i = 0; i < 65535; i++) {
  // infinite loop
}

When I change the code, it loops as well:

const int32_t t = 65535;
for (int16_t i = 0; i < t; i++) {
  // infinite loop
}

But when I make it uint32_t instead of int32_t, it actually exits:

const uint32_t t = 65535;
for (int16_t i = 0; i < t; i++) {
  // actually exits
}

Is this due to some compiler trick it does for me? I assume when I do the comparison:

i < t

For the last version it does auto convert for me? But I don't understand why it exits in the last version still...

Barmar
  • 741,623
  • 53
  • 500
  • 612
WhatABeautifulWorld
  • 3,198
  • 3
  • 22
  • 30
  • ++ is an assembly like instruction, so just after 32767 you will see -1, -2, ..- as you may expect < is cmp instruction (in fact - ) and loop never ends – Victor Gubin Mar 27 '18 at 21:00
  • @VictorGubin His question is about the last version, which *does* end. – Barmar Mar 27 '18 at 21:00
  • Your title is misleading. Your question is about the loop that isn't infinite. – Barmar Mar 27 '18 at 21:01
  • BTW, you should increase your warnings level https://wandbox.org/permlink/Igrl9FdrbP1TxyJB – Bob__ Mar 27 '18 at 21:07

1 Answers1

5

When you compare a signed type and unsigned type, the signed type is promoted to an an unsigned type before the comparison is performed. Hence, the last block of code works.


Take a look at the following program

#include <iostream>
#include <cstdint>

int main()
{
   const uint32_t t = 65535;
   int16_t i = 32765;
   for (; i < t; i++)
   {
      std::cout << i << ", " << static_cast<uint32_t>(i) << std::endl;
   }
   std::cout << i << ", " << static_cast<uint32_t>(i) << std::endl;
}

and its output with g++ 6.4.0

32765, 32765
32766, 32766
32767, 32767
-32768, 4294934528

At the point of integer overflow, the value of i flips from the maximum value to the minimum value. More importantly, when that number is promoted to uint32_t, it appears to be a large value.

The binary representation of 4294934528 in uint32_t is:

11111111 11111111 10000000 00000000

The binary representation of -32768 in int16_t is:

                  10000000 00000000

There seems to be a correlation there although I don't fully understand what the rules of promoting an signed type to an unsigned type are to explain the correlation.

R Sahu
  • 204,454
  • 14
  • 159
  • 270
  • 1
    There's no undefined behaviour here, if `int` is 32-bit. The `int16_t` will be promoted to `int` before `++` happens – M.M Mar 27 '18 at 21:58
  • @M.M, You are probably right but https://stackoverflow.com/questions/16188263/is-signed-integer-overflow-still-undefined-behavior-in-c. – R Sahu Mar 27 '18 at 22:05
  • @RSahu what part of that question do you refer to? After the promotion, incrementing the int `32767` yields the int `32768` , there is no overflow – M.M Mar 27 '18 at 22:08
  • @M.M, doesn't that count as overflow for a variable of type `int16_t`? – R Sahu Mar 27 '18 at 22:12
  • 1
    @RSahu the *integer promotions* promote the value to `int` before the increment is applied – M.M Mar 27 '18 at 22:14
  • @M.M, and the resulting `int` is simply truncated to 16 bits before it is assigned to the variable? – R Sahu Mar 27 '18 at 22:16
  • 2
    @M.M and then the `int` value is assigned to `i` which is `int16_t`. If the value is too large to be represented it will give implementation-defined behaviour. http://en.cppreference.com/w/cpp/language/implicit_conversion – PeterSW Mar 27 '18 at 22:17