Getting the subtraction of unsigned variables

Hi,

I was wondering of the case in subtracting unsigned variables, if the case variable 1 is less than variable 2.

The reason for this is that in the case of using micros() and doing the math of subtraction, and then micros() has overflowed.

Then if there is no typecast for the subtraction operation, then the Arduino will treat them as signed variables and the result will have the -ve sign.

For example;

void setup() {
  // put your setup code here, to run once:
Serial.begin(9600);

uint8_t a = 1;
uint8_t b = 8;
Serial.println((uint8_t)(a-b));
}

void loop() {
  // put your main code here, to run repeatedly:

}

So, my question now, is that if I didn't do the typecast, will the Arduino treat the typecast result as the non-typecast one ?

For example;

is this:
Serial.println(-7);

the same as this:
Serial.println((uint8_t)-7);

Something odd with the print() function. If a and b are declared as uint16_t or uint32_t, then the result prints as a large positive number, as expected, while with uint8_t the result prints as a negative number.

1 Like

The result of Serial.print depends on how the function is overloaded, and you would have to look at the Arduino core code to see what the possibilities are. There are known bugs (or features) associated with printing 32 bit integer variables (at least, on AVR Arduinos), and I would not be surprised to learn about others.

I'm guessing that your real question has to do with micros() or the like overflowing.
If so, what is the question?

1 Like

I've finding some references that indicate uint8_t gets promoted to int before an arithmetic operation, with the result then being an int.

1 Like

If you first assign the result of the calculation to a variable, you get the expected (unsigned) result as well.

void setup()
{
  Serial.begin(115200);

  uint8_t a = 1;
  uint8_t b = 8;
  uint8_t c = a - b;
  Serial.println(c);
  Serial.println(a-b);
}

void loop()
{
}

I can however not explain it.

I'm guessing that your real question has to do with micros() or the like overflowing.
If so, what is the question?

My question is that why even when I declared the variables as uint8_t the result is negative ? I must typecast.

My other question is in this case if I decremented 1 - 8 for example and the result is -7, if I typecast the result it would be 249. My question at this point will the Arduino treat -7 like 249 ? will both numbers have the same output ?

And you're right, even in micros(), if the micros() overflowed and became counting from 0 and the last variable read is still a big number, for example:

uint32_t last_micros_read = 4294000000;
uint32_t period = 1000; // e.g. 1 milli second time interval

// so the usual time math would be
// in this case let's assume that micros() has overflowed and started counting from 0 and reach 100 for example

if(micros() - last_micros_read >= period){
}

so if we did the math the answer will be
100 - 4294000000 = -4293999900

so the answer is negative, and if we typecast the answer we will get:
(uint32_t)(100 - 4294000000) = 967396

The Arduino program, when performing mathematical operations, strictly obeys the rules of the C/C++ programming language.

The output of Serial.print() sometimes defies expectations, due to oversights or coding errors in the Arduino core, or perhaps your misunderstanding of the math rules.

So take Serial.print() output with a grain of salt, as I do. It is far from perfect.

1 Like

The problem is not with print(), but with the compiler promoting the uint8_t to int before doing the arithmetic. A bit of searching on google gets into promotion rules for C language, and having anything smaller than an int converted into an int before performing an operation such as subtraction. The smallest form of an int that can fully represent any value in a uint8_t is a signed int, so the promotion is to an int instead of an unsigned int.

Any unsigned integer of at least 16 bits works on an UNO.

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

  uint8_t a = 1;
  uint8_t b = 8;
  Serial.print("uint8_t subtraction : ");
  Serial.println(a - b);
  if ((a - b) > 2) {
    Serial.println("positive result");
  } else {
    Serial.println("negative result");
  }
  Serial.println();

  uint16_t c = 1;
  uint16_t d = 8;
  Serial.print("uint16_t subtraction : ");
  Serial.println(c - d);
  if ((c - d) > 2) {
    Serial.println("positive result");
  } else {
    Serial.println("negative result");
  }
  Serial.println();

  uint32_t e = 1;
  uint32_t f = 8;
  Serial.print("uint32_t subtraction : ");
  Serial.println(e - f);

  if ((e - f) > 2) {
    Serial.println("positive result");
  } else {
    Serial.println("negative result");
  }
  Serial.println();
}

void loop() {
}

Output from this code:

uint8_t subtraction : -7
negative result

uint16_t subtraction : 65529
positive result

uint32_t subtraction : 4294967289
positive result

The same code, run on an UNO R4 WiFi gives a different output, because an int is 4 bytes instead of 2. (which might be a cause for some pre-existing code to suddenly have problems when run on an R4)

uint8_t subtraction : -7
negative result

uint16_t subtraction : -7
negative result

uint32_t subtraction : 4294967289
positive result
2 Likes

No, the subtraction and resulting answer should all be done using unsigned 32-bit integers, with the correct positive result of 967396, as long as the size of an integer is 32 bits or less on the processor you are using.

2 Likes

There are problems with print(). This particular example may well not be one of them.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.