Skip to content

Arithmetic Error on Arduino Nano #8580

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
jmsmdy opened this issue Feb 26, 2019 · 5 comments
Closed

Arithmetic Error on Arduino Nano #8580

jmsmdy opened this issue Feb 26, 2019 · 5 comments
Labels
Architecture: AVR Applies only to the AVR microcontrollers (Uno, etc.)

Comments

@jmsmdy
Copy link

jmsmdy commented Feb 26, 2019

Bug Description

For unsigned 8-bit integers on Arduino Nano compiled with the Arduino DE, nesting operators leads to an unexpected result inconsistent with using intermediary variables.

The code:

k = (i - 1) % 32;

sets k to value 255 (a seemingly incorrect value) if i=0.

Meanwhile, the code:

j = i - 1;
k = j % 32;

sets k to 31 (the correct value) if i=0.

All of these variables are type uint8_t.

I'm aware that arithmetic operations can sometimes have unexpected results (like sometimes % can return negative remainders rather than positive remainders on negative inputs). However, in this case the only difference between these two pieces of code is that in the latter we are using an intermediary variable to store the result of (i-1), and in the former we are just nesting the operators. But nesting operators versus using intermediate variables should always return the same result!

And in any case, the result of a x % 32 operation should always be a number with absolute value < 32.

Code example: (Tested on Arduino Nano with the old bootloader)

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

uint8_t i = 0;
uint8_t j = 0;
uint8_t k = 0;

void loop() {
    delay(200);
    
    j = (i - 1);
    k = (i - 1) % 32;
    
    Serial.print("i: ");
    Serial.println(i, DEC);
    Serial.print("j: ");
    Serial.println(j, DEC);
    Serial.print("k: ");
    Serial.println(k, DEC);
    Serial.println("---------------------------------");
    
    i++;
}

The output from the Serial port is:

15:18:07.623 -> i: 0
15:18:07.623 -> j: 255
15:18:07.623 -> k: 255
15:18:07.623 -> ---------------------------------
15:18:07.822 -> i: 1
15:18:07.822 -> j: 0
15:18:07.822 -> k: 0
15:18:07.822 -> ---------------------------------
15:18:08.021 -> i: 2
15:18:08.021 -> j: 1
15:18:08.021 -> k: 1
15:18:08.021 -> ---------------------------------
15:18:08.221 -> i: 3
15:18:08.221 -> j: 2
15:18:08.221 -> k: 2
   ....
15:18:58.258 -> i: 252
15:18:58.258 -> j: 251
15:18:58.258 -> k: 27
15:18:58.258 -> ---------------------------------
15:18:58.458 -> i: 253
15:18:58.458 -> j: 252
15:18:58.458 -> k: 28
15:18:58.458 -> ---------------------------------
15:18:58.657 -> i: 254
15:18:58.657 -> j: 253
15:18:58.657 -> k: 29
15:18:58.657 -> ---------------------------------
15:18:58.856 -> i: 255
15:18:58.856 -> j: 254
15:18:58.856 -> k: 30
15:18:58.856 -> ---------------------------------
15:18:59.055 -> i: 0
15:18:59.055 -> j: 255
15:18:59.055 -> k: 255
15:18:59.055 -> ---------------------------------
15:18:59.254 -> i: 1
15:18:59.254 -> j: 0
15:18:59.254 -> k: 0
15:18:59.254 -> ---------------------------------
15:18:59.454 -> i: 2
15:18:59.454 -> j: 1
15:18:59.454 -> k: 1
15:18:59.454 -> ---------------------------------
15:18:59.653 -> i: 3
15:18:59.653 -> j: 2
15:18:59.653 -> k: 2
15:18:59.653 -> ---------------------------------
   ...

Now replace these lines:

    j = (i - 1);
    k = (i - 1) % 32;

With these lines:

    j = (i - 1);
    k = j % 32;

Then we get, as expected:

15:27:15.170 -> i: 0
15:27:15.170 -> j: 255
15:27:15.170 -> k: 31
15:27:15.170 -> ---------------------------------
15:27:15.370 -> i: 1
15:27:15.370 -> j: 0
15:27:15.370 -> k: 0
15:27:15.370 -> ---------------------------------
15:27:15.569 -> i: 2
15:27:15.569 -> j: 1
15:27:15.569 -> k: 1
15:27:15.569 -> ---------------------------------
15:27:15.769 -> i: 3
15:27:15.769 -> j: 2
15:27:15.769 -> k: 2
   ...
15:28:05.980 -> i: 253
15:28:05.980 -> j: 252
15:28:05.980 -> k: 28
15:28:05.980 -> ---------------------------------
15:28:06.180 -> i: 254
15:28:06.180 -> j: 253
15:28:06.180 -> k: 29
15:28:06.180 -> ---------------------------------
15:28:06.379 -> i: 255
15:28:06.379 -> j: 254
15:28:06.379 -> k: 30
15:28:06.379 -> ---------------------------------
15:28:06.579 -> i: 0
15:28:06.579 -> j: 255
15:28:06.579 -> k: 31
15:28:06.579 -> ---------------------------------
15:28:06.778 -> i: 1
15:28:06.778 -> j: 0
15:28:06.778 -> k: 0
15:28:06.778 -> ---------------------------------
15:28:07.011 -> i: 2
15:28:07.011 -> j: 1
15:28:07.011 -> k: 1
15:28:07.011 -> ---------------------------------
15:28:07.210 -> i: 3
15:28:07.210 -> j: 2
15:28:07.210 -> k: 2
 ...

Possible Explanations?

One explanation is that this is some kind of hardware bug.

Another explanation is that there is some compiler optimization which is incorrectly replacing (i - 1) % 32 with another operation which returns the wrong value.

@facchinm
Copy link
Member

facchinm commented Mar 4, 2019

@jmsmdy would you mind testing the same sketch using the beta toolchain (instructions here #7949 (comment)) ?

@facchinm facchinm added Waiting for feedback More information must be provided before we can proceed Architecture: AVR Applies only to the AVR microcontrollers (Uno, etc.) labels Mar 4, 2019
@jmsmdy
Copy link
Author

jmsmdy commented Mar 4, 2019

I'm having trouble compiling the staging branch of toolchain-avr on github (there was some python error). I'll give it another try when I have free time. If anyone who has the staging toolchain can test the code I provided on a Nano, that would be much appreciated.

@per1234
Copy link
Collaborator

per1234 commented Mar 4, 2019

There's no need to compile the staging branch to test the beta toolchain. You only need to install it via the Arduino IDE's Boards Manager:

  1. File > Preferences
  2. To the "Additional Boards Manager URLs" field, add the URL: http://downloads.arduino.cc/packages/package_avr_7.3.0_index.json. If you have multiple URLs in that field, separate them with commas.
  3. Click "OK".
  4. Tools > Board > Boards Manager
  5. Wait for downloads to finish.
  6. Click on "Arduino AVR Boards".
  7. Click "Update". You should now have Arduino AVR Boards 1.6.209 installed.
  8. Wait for update to finish.
  9. Click "Close".

I just tried your code with Arduino AVR Boards 1.6.209 and I still get the unexpected result:

i: 0
j: 255
k: 255

@jmsmdy
Copy link
Author

jmsmdy commented Mar 5, 2019

Thanks, @per1234. @facchinm, following per's instructions I tested it with the beta build using your json file to update to 1.6.209, and I am still getting the same result.

@per1234 per1234 removed the Waiting for feedback More information must be provided before we can proceed label Mar 6, 2019
@facchinm
Copy link
Member

facchinm commented Mar 6, 2019

Ok, the bug is not a bug but a side effect of wrapping arithmetics. Simple POC

#include <stdint.h>

uint8_t i = 0;
uint8_t j = 0;
uint8_t k = 0;

void main() {

    while (1) {    
    j = (i - 1);
    k = (i - 1) % 32;
    
    printf("i: %d\n", i);
    printf("j: %d\n", j);
    printf("k: %d\n", k);
    
    i++;
    if (i == 0)
        exit(0);
    }
}

(you can compile it on your desktop pc and just behaves as the nano).

The problem here is that %32 is logically equivalent to & 0x1F (that would zero out the higher bits) but this is not the same of the C standard. Moreover, If i==0, (i-1) must be promoted to a bigger type to hold the result, becoming an int (16bit in avr case filled by ones).

Using the formula here https://stackoverflow.com/a/11720975 with 0xFFFF as input returns the "expected" result.

@facchinm facchinm closed this as completed Mar 6, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Architecture: AVR Applies only to the AVR microcontrollers (Uno, etc.)
Projects
None yet
Development

No branches or pull requests

3 participants