0

I am developing under Windows 10 + VS2017 (15.8.5)

The code I have is the following

#include <iostream>

int main(int argc, char** argv)
{
    int i = 65176;
    int j = 65224;
    int k = 0;
    int res[3] = {65536, 65536};

    int index_int = i + j * (res[0] + 1) + k * (res[0] + 1)*(res[1] + 1);
    unsigned int index_uint = i + j * (res[0] + 1) + k * (res[0] + 1)*(res[1] + 1);
    size_t index_sizet = i + j * (res[0] + 1) + k * (res[0] + 1)*(res[1] + 1);

    std::cout << "sizeof(int):          " << sizeof(index_int) << " - index_int   = " << index_int << std::endl;
    std::cout << "sizeof(unsigned int): " << sizeof(index_uint) << " - index_uint  = " << index_uint << std::endl;
    std::cout << "sizeof(size_t):       " << sizeof(index_sizet) << " - index_sizet = " << index_sizet << std::endl;

    return 0;
}

which prints out

sizeof(int):          4 - index_int   = -20316832
sizeof(unsigned int): 4 - index_uint  = 4274650464
sizeof(size_t):       8 - index_sizet = 18446744073689234784

int vs unsigned int: Since the range for an int is (-2,147,483,648, 2,147,483,647) the operation for index_int overflows the limits hence the negative value, contrary to unsigned int whose range is (0 to 4,294,967,295).

unsigned int vs size_t: However, I don't understand why size_t has a whole different value, what I know is that it has a larger range because of 64 bit it has. What or how the calculation is carried out?

NathanOliver
  • 171,901
  • 28
  • 288
  • 402
BRabbit27
  • 6,333
  • 17
  • 90
  • 161
  • 1
    `signed int` overflow is undefined behavior. Only `unsigned int` overflow is supported in c++. – François Andrieux Oct 04 '18 at 15:15
  • Singed ints are a new, hot data type, fresh out of the oven? – Yakk - Adam Nevraumont Oct 04 '18 at 15:19
  • The expressions used to initialise the variables all do calculations as `int`, which overflows(giving undefined behaviour) BEFORE converting the result to the required type. – Peter Oct 04 '18 at 15:21
  • @FrançoisAndrieux Math in Z/m doesn't overflow. – Baum mit Augen Oct 04 '18 at 15:21
  • Your expression uses int variables, so the compiler uses 32-bit operators to evaluate it. After which the int value gets assigned to a size_t variable. Since it is negative you get to see how a negative signed value gets converted to an unsigned 64-bit type. Either long long variables or a cast is necessary to convince the compiler it need to use 64-bit operators. Ensure the warning level is high enough so the compiler can complain about this. – Hans Passant Oct 04 '18 at 15:23
  • Possible duplicate of [What happens if I assign a negative value to an unsigned variable?](https://stackoverflow.com/questions/2711522/what-happens-if-i-assign-a-negative-value-to-an-unsigned-variable) – apple apple Oct 04 '18 at 15:24
  • @FrançoisAndrieux since op sat he understand the value of `index_int`. I would think that answer is enough. – apple apple Oct 04 '18 at 15:26

2 Answers2

3

Your code has undefined behavior. In all cases you do

j * (res[0] + 1)

which evaluates to 65224 * 65537 which in turn is 4274585288. This value exceeds what a int with a size of 4 can hold so you get signed integer overflow which is undefined behavior.

R Sahu
  • 204,454
  • 14
  • 159
  • 270
NathanOliver
  • 171,901
  • 28
  • 288
  • 402
  • That overflow I understand, therefore using `unsigned int` gives the correct value, but then what I don't get is why using `size_t` (which it says is 64 bits) gives a whole different value from that of `unsigned int`? – BRabbit27 Oct 04 '18 at 15:21
  • @BRabbit27 Your overflowed result gets turned into a different value in a larger type. That is not so surprising. – NathanOliver Oct 04 '18 at 15:22
  • 1
    @BRabbit27 No, your therefor is wrong. It gives the correct value for `unsigned int` but the causal relationship is broken. The calculation is undefinend behavior before it goes anywhere near `unsigned int`. The cast to `unsigned int` happens due to "luck" generate the right value (where "luck" is "naive 2s complement math and overflow on your hardware happens to give the right result"; it is like reading a variable that went out of scope on the stack, you can often "get the right answer", but that is because you got "lucky"). Don't rely on it. – Yakk - Adam Nevraumont Oct 04 '18 at 15:22
  • 1
    @BRabbit27 The thing is.. Since there are no `unsigned` values inside that expression, the whole expression gets evaluated as an `int`, and then converted to the destination type. Signed `int` overflow is undefined behavior. You can't count on a particular behavior of an undefined behavior. – Algirdas Preidžius Oct 04 '18 at 15:23
  • Then I'm better of using for *i,j,k,res[]* unsigned int or size_t from the beginning to avoid any overflow when doing the actual computation? – BRabbit27 Oct 04 '18 at 15:24
  • @BRabbit27 overflow on unsigned values is math mod `2^n`. If you bound the values and can prove no overflow occurs, you are good. If you cannot, then you again can be surprised. If you need to do math without knowing enough about values to avoid overflow, you need a bignum library. – Yakk - Adam Nevraumont Oct 04 '18 at 15:26
  • @BRabbit27 Yes. use a wide enough data type so it doesn't overflow – NathanOliver Oct 04 '18 at 15:26
-1

your code does not different from (include the UB of int overflow)


int index_int = i + j * (res[0] + 1) + k * (res[0] + 1)*(res[1] + 1);
unsigned int index_uint = index_int;
size_t index_sizet = index_int;

i.e. the result comes from convert int to other type

apple apple
  • 10,292
  • 2
  • 16
  • 36
  • This has the same undefined behavior, the calculation overflows. It doesn't matter what you assign the result to. – François Andrieux Oct 04 '18 at 15:27
  • @FrançoisAndrieux So I say **no different from** (which contains that UB). Since the UB is not the point OP confuse. – apple apple Oct 04 '18 at 15:29
  • I'm not sure what your comment means, but *"the result comes from convert int to other type"* is not correct. – François Andrieux Oct 04 '18 at 15:30
  • @FrançoisAndrieux the UB effectively create `-20316832` at OP's question. and this convert to `4274650464` and `18446744073689234784`, what part you don't understand? – apple apple Oct 04 '18 at 15:31