Skip to content

Erratic INT Roll Over #2207

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
HowardParr opened this issue Jul 23, 2014 · 15 comments
Closed

Erratic INT Roll Over #2207

HowardParr opened this issue Jul 23, 2014 · 15 comments
Assignees
Milestone

Comments

@HowardParr
Copy link

While trying to troubleshoot a problem I was having with INT roll over I ran across the following erratic behavior that I can not explain. Can anyone shed sone light on what's happening here?

I tried the following code on the Arduino Mega 2560:

void setup() {
int x = 32766;

Serial.begin(19200);
for (int a = 0; a < 5; a++) {
Serial.print(x);
Serial.print("\t");
Serial.println(x, BIN);
x += 1;
}
}

void loop() {
}

and received the following output:

32766 111111111111110
32767 111111111111111
32767 111111111111111
32767 111111111111111
32767 111111111111111

I was expecting the following:

32766 111111111111110
32767 111111111111111
-32768 1000000000000000
-32767 1000000000000001
-32766 1000000000000010

When I changed the initial value of x from 32766 to 32765 (one less) I got the following output:

32765 111111111111101
32766 111111111111110
32767 111111111111111
-32768 11111111111111111000000000000000
-32767 11111111111111111000000000000001

The decimal representation was now correct however, the binary representation has 16 additional significant bits.

Can anyone explain why I'm getting these results?

@matthijskooijman
Copy link
Collaborator

Weird. I can't explain why the incrementing doesn't work... What Arduino version are you using? Could you try 1.5.7 (which has a newer compiler).

As for the extra binary digits, I think it is because the print function that actually does the printing work takes a long, and all other functions forward to there:

https://github.com/arduino/Arduino/blob/master/hardware/arduino/cores/arduino/Print.cpp#L76

The cast to long triggers sign-extension, which copies the topmost (sign) bit into the new bits introduced by the cast.

@HowardParr
Copy link
Author

I'm still using the official 1.0.5 release. The 1.5.7 release is still in Beta, but I'll give it a shot.

@HowardParr
Copy link
Author

I've tried to download the installer three times and each time the "Installer integrity check" failed. I downloaded the .zip file but there was no setup file in it and no instructions on where the files should be placed.

@MrScarabus
Copy link

Using nightly build + UNO.
On 32766 same result, but on 32765 my output was (also appended value of a):

□□□□32765 111111111111101 0
32766 111111111111110 1
32767 111111111111111 2
-32768 11111111111111111000000000000000 3
-32767 11111111111111111000000000000001 4
-32766 11111111111111111000000000000010 5
-32765 11111111111111111000000000000011 6
-32764 11111111111111111000000000000100 7
...

For some reason it can't break the loop...

@HowardParr
Copy link
Author

In further testing last night I also ran into the issue where the "for" loop did not terminate as expected. As these are two different variables, I wonder if the compiler is causing the value of the loop iterator to be overwritten by the issue we are seeing in the roll over failures.

@HowardParr
Copy link
Author

I modified the code a little to check not only the ABS function but the comparison operator as I was having problems with it as well:

void setup() {
  int x = -4;

  Serial.begin(19200);
  for (int a = 0; a < 10; a++) {
    Serial.print(x);
    Serial.print("\t");
    Serial.print(abs(x));
    Serial.print("\t");
    if (x<0) {
      Serial.print("Neg"); }
    else {
      Serial.print("Pos"); }
    Serial.print("\t");
    Serial.println(x, BIN);
    x += 1;
  }
}

void loop() {
}

When incrementing a negative number to a positive number the roll over works as expected:

-4 4 Neg 11111111111111111111111111111100
-3 3 Neg 11111111111111111111111111111101
-2 2 Neg 11111111111111111111111111111110
-1 1 Neg 11111111111111111111111111111111
0 0 Pos 0
1 1 Pos 1
2 2 Pos 10
3 3 Pos 11
4 4 Pos 100
5 5 Pos 101

As does decrementing from a positive number to a negative number:

5 5 Pos 101
4 4 Pos 100
3 3 Pos 11
2 2 Pos 10
1 1 Pos 1
0 0 Pos 0
-1 1 Neg 11111111111111111111111111111111
-2 2 Neg 11111111111111111111111111111110
-3 3 Neg 11111111111111111111111111111101
-4 4 Neg 11111111111111111111111111111100

However, when incrementing from a positive number to a negative number the roll over appears to work but the ability of both the ABS function and the Less Than operator appear to be unable to ascertain the sign of the variable:

32763 32763 Pos 111111111111011
32764 32764 Pos 111111111111100
32765 32765 Pos 111111111111101
32766 32766 Pos 111111111111110
32767 32767 Pos 111111111111111
-32768 -32768 Pos 11111111111111111000000000000000
-32767 -32767 Pos 11111111111111111000000000000001
-32766 -32766 Pos 11111111111111111000000000000010
-32765 -32765 Pos 11111111111111111000000000000011
-32764 -32764 Pos 11111111111111111000000000000100

Furthermore, when decrementing from a negative number to a positive number, again the ABS function and the Less Than operator appear to be unable to ascertain the sign of the variable. What's worse is that it also appears to interfere with the FOR loop as the FOR loop does not end after the 10 iterations programmed:

-32764 32764 Neg 11111111111111111000000000000100
-32765 32765 Neg 11111111111111111000000000000011
-32766 32766 Neg 11111111111111111000000000000010
-32767 32767 Neg 11111111111111111000000000000001
-32768 -32768 Neg 11111111111111111000000000000000
32767 -32767 Neg 111111111111111
32766 -32766 Neg 111111111111110
32765 -32765 Neg 111111111111101
32764 -32764 Neg 111111111111100
32763 -32763 Neg 111111111111011
32762 -32762 Neg 111111111111010
32761 -32761 Neg 111111111111001
32760 -32760 Neg 111111111111000
32759 -32759 Neg 111111111110111
32758 -32758 Neg 111111111110110
32757 -32757 Neg 111111111110101
.....

Letting the program run until it rolls over again it appears that the ABS function and the Less Than operator are again working:

.....
4 -4 Neg 100
3 -3 Neg 11
2 -2 Neg 10
1 -1 Neg 1
0 0 Neg 0
-1 1 Neg 11111111111111111111111111111111
-2 2 Neg 11111111111111111111111111111110
-3 3 Neg 11111111111111111111111111111101
-4 4 Neg 11111111111111111111111111111100
-5 5 Neg 11111111111111111111111111111011
.....

It appears that this issue not only affects the x variable but it also corrupts something in the FOR loop. Is there anyone who can dig into this a little deeper than I can?

@JacobChrist
Copy link

Add the following into your loop:

Serial.print("\t");
Serial.println(abs(x), BIN);

Maybe it will add clarity. Possibly abs() returns a 32bit value and
.print(x,DEC) expects a signed 16 bit. Also, it looks like the
.print(x,BIN) can handle/expects 32bit values.

On Thu, Jul 24, 2014 at 12:26 PM, HowardParr notifications@github.com
wrote:

I modified the code a little to check not only the ABS function but the
comparison operator as I was having problems with it as well:

void setup() {
int x = -4;

Serial.begin(19200);
for (int a = 0; a < 10; a++) {
Serial.print(x);
Serial.print("\t");
Serial.print(abs(x));
Serial.print("\t");
if (x<0) {
Serial.print("Neg"); }
else {
Serial.print("Pos"); }
Serial.print("\t");
Serial.println(x, BIN);
x += 1;
}
}

void loop() {
}

When incrementing a negative number to a positive number the roll over
works as expected:

-4 4 Neg 11111111111111111111111111111100
-3 3 Neg 11111111111111111111111111111101
-2 2 Neg 11111111111111111111111111111110
-1 1 Neg 11111111111111111111111111111111
0 0 Pos 0
1 1 Pos 1
2 2 Pos 10
3 3 Pos 11
4 4 Pos 100
5 5 Pos 101

As does decrementing from a positive number to a negative number:

5 5 Pos 101
4 4 Pos 100
3 3 Pos 11
2 2 Pos 10
1 1 Pos 1
0 0 Pos 0
-1 1 Neg 11111111111111111111111111111111
-2 2 Neg 11111111111111111111111111111110
-3 3 Neg 11111111111111111111111111111101
-4 4 Neg 11111111111111111111111111111100

However, when incrementing from a positive number to a negative number the
roll over appears to work but the ability of both the ABS function and the
Less Than operator appear to be unable to ascertain the sign of the
variable:

32763 32763 Pos 111111111111011
32764 32764 Pos 111111111111100
32765 32765 Pos 111111111111101
32766 32766 Pos 111111111111110
32767 32767 Pos 111111111111111
-32768 -32768 Pos 11111111111111111000000000000000
-32767 -32767 Pos 11111111111111111000000000000001
-32766 -32766 Pos 11111111111111111000000000000010
-32765 -32765 Pos 11111111111111111000000000000011
-32764 -32764 Pos 11111111111111111000000000000100

Furthermore, when decrementing from a negative number to a positive
number, again the ABS function and the Less Than operator appear to be
unable to ascertain the sign of the variable. What's worse is that it also
appears to interfere with the FOR loop as the FOR loop does not end after
the 10 iterations programmed:

-32764 32764 Neg 11111111111111111000000000000100
-32765 32765 Neg 11111111111111111000000000000011
-32766 32766 Neg 11111111111111111000000000000010
-32767 32767 Neg 11111111111111111000000000000001
-32768 -32768 Neg 11111111111111111000000000000000
32767 -32767 Neg 111111111111111
32766 -32766 Neg 111111111111110
32765 -32765 Neg 111111111111101
32764 -32764 Neg 111111111111100
32763 -32763 Neg 111111111111011
32762 -32762 Neg 111111111111010
32761 -32761 Neg 111111111111001
32760 -32760 Neg 111111111111000
32759 -32759 Neg 111111111110111
32758 -32758 Neg 111111111110110
32757 -32757 Neg 111111111110101
.....

Letting the program run until it rolls over again it appears that the ABS
function and the Less Than operator are again working:

.....
4 -4 Neg 100
3 -3 Neg 11
2 -2 Neg 10
1 -1 Neg 1
0 0 Neg 0
-1 1 Neg 11111111111111111111111111111111
-2 2 Neg 11111111111111111111111111111110
-3 3 Neg 11111111111111111111111111111101
-4 4 Neg 11111111111111111111111111111100
-5 5 Neg 11111111111111111111111111111011
.....

It appears that this issue not only affects the x variable but it also
corrupts something in the FOR loop. Is there anyone who can dig into this a
little deeper than I can?


Reply to this email directly or view it on GitHub
#2207 (comment).

Jacob Christ
ProLinear/PONTECH, Inc.
1-877-985-9286 Phone
1-413-235-1651 Fax
http://www.pontech.com

@HowardParr
Copy link
Author

Thanks for the input.

In a previous response it was shown that the print function casts the value passed as a long. And when printing, the leading zeros are always omitted. So when any negative 16 bit number is cast as long, 16 1 bits are added. So i now understand why the print(x, BIN) displays as it does.

The issue remaining is why the ABS and Less Than operator can not distinguish the fact that in this particular roll over scenario the value has changed from positive to a negative (or vice versa).

@JacobChrist
Copy link

Yeah, that is wierd. What if you cast 0 as an int?

If (x <(int) 0)

Jacob
On Jul 24, 2014 9:31 PM, "HowardParr" notifications@github.com wrote:

Thanks for the input.

In a previous response it was shown that the print function casts the
value passed as a long. And when printing, the leading zeros are always
omitted. So when any negative 16 bit number is cast as long, 16 1 bits are
added. So i now understand why the print(x, BIN) displays as it does.

The issue remaining is why the ABS and Less Than operator can not
distinguish the fact that in this particular roll over scenario the value
has changed from positive to a negative (or vice versa).


Reply to this email directly or view it on GitHub
#2207 (comment).

@HowardParr
Copy link
Author

I know that if you want a literal to be cast as a float you would use "0.0", and I believe that using just "0" the literal is automatically cast as an integer. Other than that I don't believe you can actually "cast" a literal.

@matthijskooijman
Copy link
Collaborator

Sure you can. (char)0 and (int)0 are different things (though the latter is the same as just 0). But literals are not always int, they can be bigger. E.g 66000 is a long, so it's different from (int)66000 (which overflows, btw).

Note sure what the point of the casting is - just responding to the last comment.

@JacobChrist
Copy link

My thought on adding the cast is just to test if there is a problem with
the compiler default casts.

Jacob
On Jul 28, 2014 8:06 AM, "Matthijs Kooijman" notifications@github.com
wrote:

Sure you can. (char)0 and (int)0 are different things (though the latter
is the same as just 0). But literals are not always int, they can be
bigger. E.g 66000 is a long, so it's different from (int)66000 (which
overflows, btw).

Note sure what the point of the casting is - just responding to the last
comment.


Reply to this email directly or view it on GitHub
#2207 (comment).

@matthijskooijman
Copy link
Collaborator

Ah! I figured it out. Overflow is not defined for signed integers, so once you overflow a signed int, the behaviour is undefined. In this case, aggressive loop optimization, combined with the overflow, causes things to go haywire. In particular, the loop never terminates when I tried the example here, which is weird. I reduces this to a smaller source file:

$ cat test.c
void foo(int);

void bar() {
  int x = 32763;

  for (int a = 0; a < 10; a++) {
    foo(x);
    x += 1;
  }
}

$ avr-gcc -c -Os test.c -o test.o
test.c: In function ‘void bar()’:
test.c:8:11: warning: iteration 4u invokes undefined behavior [-Waggressive-loop-optimizations]
     x += 1;
           ^
test.c:6:3: note: containing loop
   for (int a = 0; a < 10; a++) {
   ^
matthijs@grubby:~/docs/Electronics/Arduino/Sketches/Test$ avr-objdump -j .text -D foo.o

foo.o:     file format elf32-avr


Disassembly of section .text:

00000000 <bar>:
   0:   cb ef           ldi     r28, 0xFB       ; 251
   2:   df e7           ldi     r29, 0x7F       ; 127
   4:   8c 2f           mov     r24, r28
   6:   9d 2f           mov     r25, r29
   8:   00 d0           rcall   .+0             ; 0xa <bar+0xa>
   a:   21 96           adiw    r28, 0x01       ; 1
   c:   00 c0           rjmp    .+0             ; 0xe <__zero_reg__+0xd>
$ avr-objdump -j .text -D test.o

test.o:     file format elf32-avr


Disassembly of section .text:

00000000 <bar>:
   0:   cb ef           ldi     r28, 0xFB       ; 251
   2:   df e7           ldi     r29, 0x7F       ; 127
   4:   8c 2f           mov     r24, r28
   6:   9d 2f           mov     r25, r29
   8:   00 d0           rcall   .+0             ; 0xa <bar+0xa>
   a:   21 96           adiw    r28, 0x01       ; 1
   c:   00 c0           rjmp    .+0             ; 0xe <__zero_reg__+0xd>

I guess the problem here really is that warnings are disabled in the IDE, which is the subject of #1728.

@ffissore
Copy link
Contributor

I've added a new preference "show all compiler warnings" that turns warnings on
Available as nightly builds in the next few hours

oriregev added a commit to oriregev/Arduino that referenced this issue Apr 27, 2015
* upstream/master: (239 commits)
  Fix for issue arduino#292
  Windows: build_pull_request needed to be upgraded as well
  Update revisions.txt
  Windows: JRE is chosen at build time via WINDOWS_BUNDLED_JVM property
  Update Tone.cpp
  Update revisions.txt
  Block discovery threads until packages is not null, otherwise boards discovered during startup will miss model name
  Also SerialDiscovery was affected by bug found at 40535df. Fixes arduino#2892
  NetworkDiscovery was silently failing because packages werenìt ready yet. Fixes arduino#2837
  Better preference for setting warnings level. See arduino@61592d7#commitcomment-10668365
  SAM boards stop compiling due to way of handling params with spaces on different OSs. Fixed
  Update Tone.cpp
  Restored error messages. Got rid of MessageSyphon as ther were losing some error messages. Fixes arduino#2737
  Windows: added listComPorts test case
  New preference: enable all compiler warnings, off by default. Fixes arduino#1728 and arduino#2415. Also affects arduino#2634 and arduino#2207
  build.xml: spreading failonerror on all exec tasks, it's better to crash early
  Lib/Board Manager CRC check is now case insensitive. Fixes arduino#2953
  License fix to audio library
  License fix
  Library Manager: better error message
  ...
@matthijskooijman
Copy link
Collaborator

Seems this issue is figured out, and warnings can be re-enabled now, so I'm closing this issue.

@ffissore ffissore modified the milestone: Release 1.6.6 Aug 3, 2015
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants