Overflowed value prints non-overflowed value on Arduino Uno

I ran into a rather bizarre problem, I posted it to stackoverflow but maybe someone on here has an idea about it, or knows where to file a bug report.

The problem is also posted on Stackoverflow:
Overflowed value prints non-overflowed value on Arduino Uno

The board used is an Arduino Uno, with IDE 1.8.1 and avr package 1.6.17 and 1.6.23(see edit2 for even weirder output).

The problem

First of all, I know the problem with the code and how to get it to work. I'm mainly looking for an explanation why my output is what it is.

The following piece of code replicates the behaviour:

void setup() {
  Serial.begin(9600);
}

void loop() {
  Serial.println("start");
  for(int i = 0; i < 70000; i++) {
    if((i % 2000) == 0)
      Serial.println(i);
  }
}

Obviously the for loop will run forever because i will overflow at 32,767. I would expect it to overflow and print -32000.

Expected|   Actually printed
0       |   0                                       
2000    |   2000                                    
4000    |   4000                                    
...     |   ...                                     
30000   |   30000                                   
32000   |   32000                                   
-32000  |   33536                                   
-30000  |   35536

It looks like it prints the actual iterations, since if you overflow and count to -32000 you would have 33536 iterations, but I can't figure out how it's able to print the 33536.

The same thing happens every 65536 iterations:

95536   |   161072  |   226608  |   292144  |   357680
97536   |   163072  |   228608  |   294144  |   359680
99072   |   164608  |   230144  |   295680  |   361216
101072  |   166608  |   232144  |   297680  |   363216

EDIT
When I change the loop to add 10.000 every iteration and only print every 1.000.000 to speed it up the Arduino crashes (or at least, the prints stop) at '2.147.000.000'. Which seems to point to the 32-bit idea of svtag**

EDIT2

The edits I made for the 2.147.000.000-check:

void loop() {
  Serial.println("start");
  for(int i = 0; i < 70000; i+=10000) {
    if((i % 1000000) == 0)
      Serial.println(i);
  }
}

They work in the same trend with the previous example, printing 0, 1000000, 2000000,...

However, when I update the AVR package from 1.6.17 to the latest 1.6.23 I get only 0's. The original example (% 2000 & i++) still gives the same output.

Please post your problem code here

Added the post from SO to my post. It looks like the int is used as a 16 bit in the loop but somehow has some sort of shadowcopy that is 32 bit and that is used in the print function. I have no clue how and why that would happen.

Declare i as long, and use 70000L in place of 70000. No point worrying about what kind of
undefined behaviour you get when you do something that is undefined - the compiler is
free to print "kjsdcnsjcnsd" if it wants in such circumstances!

I honestly don't see how this is 'undefined' but sure, changed the code:

void loop() {
  int i = 0;
  while(true) {
    i++;
    if((i % 2000) == 0)
      Serial.println(i);
  }
}

This has the exact same output as the one I posted above, and isn't as undefined as above.

PS. I do realise what I have to change to have the defined behaviour, but this shouldn't be happening imo.

I too experience the problem but it seems to be associated with using 'int' for i. If I change that to 'unsigned int' the problem seems to go away !

That is odd. I think your assumption about a shadow copy is makes sense, but you're going to have to dump the compiled code to really see. Interestingly, if you make i volatile, you get the expected behavior, which hints that i's living in registers.

stowite:
I too experience the problem but it seems to be associated with using 'int' for i. If I change that to 'unsigned int' the problem seems to go away !

Thanks! I experience the same behaviour now, 'unsigned int' seems to 'fix' the problem, as does 'uint16_t'. 'int16_t' and 'int' do give the problem.

With that in mind I also tested 'uint8_t' and 'int8_t', which both work as one would expect. With 'int32_t' the program still crashes at 2.147.000.000 which is also kind of strange as I'd expect it to overflow to -2.147.000.000 (I don't know the last 6 digits by heart), but it doesn't seem to have the same behaviour as the 16bit.

So it is apparently indeed tied to the 16bit signed integer value..

wildbill:
That is odd. I think your assumption about a shadow copy is makes sense, but you're going to have to dump the compiled code to really see. Interestingly, if you make i volatile, you get the expected behavior, which hints that i's living in registers.

How would I get the compiled code?

I've never needed to do it, but here's an old forum thread on the topic: How can one view the assembly code output from the Arduino IDE? - Programming Questions - Arduino Forum