The result of 512*1024 becomes 0

I ran the following code.

#include <SoftwareSerial.h>

//SoftwareSerial USB_HOST_SERIAL = SoftwareSerial(2, 3); // RX, TX

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

  delay(100);
}

void loop() {// put your main code here, to run repeatedly:
  unsigned long int k = 512 * 1024;
  //unsigned long int k = 524288;
  Serial.println(k);
  delay(100);

}

It was expected to be displayed 524288 in serial monitor.
However, what I got is 0 like below.

512_1024_result

It would be helpful if you tell me which portion is wrong in my code.

〇My environment
Arduino IDE 1.8.15 + Arduino Uno R3
This problem was observed with Arduino Pro Miro 3.3V 8MHz also.

try just "unsigned long"

Edit: that's not it

I'm playing with it, but not using the library

Thank you for your response.
I tried that, but the result was the same.

#include <SoftwareSerial.h>

//SoftwareSerial USB_HOST_SERIAL = SoftwareSerial(2, 3); // RX, TX

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

  delay(100);
}


void loop() {// put your main code here, to run repeatedly:
  unsigned long k = 512 * 1024;
  //unsigned long int k = 524288;
  Serial.println(k);
  delay(100);

}

512_1024_result2

Cast it as a long

k = (long)  512 * 1024;
1 Like

512 and 1024 are both less than 32767 so they are both treated as 'int'. The multiply causes an integer overflow leaving a result of 0. That 0 is then converted to 'unsigned long int'.

To have the math done in unsigned long int, use unsigned long int constants:
unsigned long int k = 512ul * 1024ul;

Note: The arguments to the multiply will have the 'usual conversions' applied. You only need one of the two to be 'unsigned long int' for the other to be converted to 'unsigned long int' before the multiply.

2 Likes

unsigned long int k = 512 * 1024ul;

From the Arduino reference on Integer Constants.
https://www.arduino.cc/reference/en/language/variables/constants/integerconstants/

Notes and Warnings
U & L formatters:

By default, an integer constant is treated as an int with the attendant limitations in values. To specify an integer constant with another data type, follow it with:

a 'u' or 'U' to force the constant into an unsigned data format. Example: 33u

a 'l' or 'L' to force the constant into a long data format. Example: 100000L

a 'ul' or 'UL' to force the constant into an unsigned long constant. Example: 32767u

1 Like

Thank you!
This solved the problem!

answer #5 is the true explanation.

1 Like

Thank you for everyone.

I understand the following.

"int * int" result becomes int.
This time, as a result of overflow result became 0.

To avoid this, casting is useful.
As a result of cast, "long * int" result becomes long.
This solved the problem.

Thank you!

Casting is useful for variables. For constants, it makes more sense to use the right type of constant rather than casting the wrong type to the right type. :slight_smile:

2 Likes

Would this compile differently or is that a syntax preference?

Thank you for your kind explanation!
I'll keep this in mind.

I expect most of the versions that produce the right answer will end up compiling to the same code. The compiler is smart enough to do the conversions and the math so, in the binary, everything will look the same as:
k = 524288;
That doesn't mean you should use "k = 524288" in your code because people reading the code will have a hard time figuring out why you are using that value. Good programming practice would be for the 1024 constant to have a useful name and the 512 constant to have a useful name.

1 Like

I took that to be a sign of this being a MRE.

actually when you have a long and an int, the int is promoted first into the larger storage (so long in that case), the multiplication is done using long, and then the result is transformed (with very clear rules) into the destination type of your variable.

Also important to know that

  • if one of the operand is unsigned and the other is signed, then the later is transformed in place into the equivalent memory representation as unsigned (ie the bits are interpreted as if the value was unsigned) for the operation.
  • if you store an unsigned value into a signed value, the bits are interpreted directly.
  • if the operant fit on a byte, it is transformed into the (signed/unsigned) int (bit equivalent) representation before doing the math.

that leads to interesting results if you are not careful. For example, on a UNO this code

void setup() {
  Serial.begin(115200); Serial.println();
  const uint8_t b = 255;
  uint8_t   x1 = b * -2; Serial.print(x1); Serial.print("\t= 0b"); Serial.println(x1, BIN);
  int16_t   x2 = b * -2; Serial.print(x2); Serial.print("\t= 0b"); Serial.println(x2, BIN);
  uint16_t  x3 = b * -2; Serial.print(x3); Serial.print("\t= 0b"); Serial.println(x3, BIN);
  int32_t   x4 = b * -2; Serial.print(x4); Serial.print("\t= 0b"); Serial.println(x4, BIN);
  uint32_t  x5 = b * -2; Serial.print(x5); Serial.print("\t= 0b"); Serial.println(x5, BIN);
}

void loop() {}

will print to the Serial monitor

2	       = 0b10
-510 	   = 0b11111111111111111111111000000010
65026  	   = 0b1111111000000010
-510	   = 0b11111111111111111111111000000010
4294966786 = 0b11111111111111111111111000000010

quite different values !!

2 Likes

Thank you for detailed explanation.

If calculation result is stored to unsigned variable, I should pay attention to signed value in the formula.

Thank you!