Problem when storing result of multiplication as a "long" variable

Hello,

After a marathon debugging session I finally discovered that my bug had to do with a data type-handling pitfall that I was not aware of. In a nutshell:

void setup() {

  Serial.begin(9600);

  long number1 = 3600 * 24;
  Serial.println(number1, DEC); // does not print the correct result
  
  long number2 = long(3600) * long(24);
  Serial.println(number2, DEC); // does print the correct result

}

void loop() {}

Please could someone explain to me why the above happens? Whilst sticking the cast operator in front of each multiplier does solve my problem, I'd like to get to the bottom of this to better understand exactly when this needs to be done and when it doesn't. It was a bit of a shock to discover that this doesn't work as I expected!

No doubt this is a classic, well documented, rookie pitfall, but I'm afraid I struggled to find relevant information on it in any data type tutorials. Instead of an explanation I would also happily be directed to a relevant webpage, or thrown a couple of good search terms.

Many thanks in advance for any help you can offer!

Ed

3600L * 24L

The reason is because numeric constants default to int. By tacking on the "L" modifier shown by AWOL, the compile knows to make the constants a long data type.

Thank you for the fast replies! These helped me to Google a bit about this and found that this is well documented on the IntegerConstants page.

There, under "U & L formatters" it says:

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:

  • 'u' or 'U' to force the constant into an unsigned data format. Example: 33u
  • 'l' or 'L' to force the constant into a long data format. Example: 100000L
  • 'ul' or 'UL' to force the constant into an unsigned long constant. Example: 32767ul

That isn't the whole story. The product of two integers (16-bits on Arduino) is defined in the C language to also be an integer - not a long (32-bit) - so you have to cast one or both integers to be a long (32-bit) so that the result of the multiplication is long.

Pete

Great, thanks for the further clarification.

Interestingly, one of the reasons why it initially took me so long to identify my mistake was because this problem does not come up if contained in the following for loop:

void setup() {

  Serial.begin(9600);

  for (int i = 0; i < 25; i++) {
    long number = 3600 * i;
    Serial.println(number, DEC); // does print the correct result
  }
}

void loop() {}

Why is this different?

The product of two integers (16-bits on Arduino) is defined in the C language to also be an integer

Please don't confuse "int" with "integer"; an unsigned long is an integer, as is a char.

Correction noted :astonished: Thanks

Pete

eddie_matos: Why is this different?

Two separate entities doing the multiplication. The compiler for the former, the microcontroller for the latter.

eddie_matos:
Great, thanks for the further clarification.

Interestingly, one of the reasons why it initially took me so long to identify my mistake was because this problem does not come up if contained in the following for loop:

void setup() {

Serial.begin(9600);

for (int i = 0; i < 25; i++) {
   long number = 3600 * i;
   Serial.println(number, DEC); // does print the correct result
 }
}

void loop() {}

However this does not:

volatile int i = 24;

void setup ()
  {
  Serial.begin (115200);
  long number = 3600 * i;
  Serial.println(number, DEC); 
  }  // end of setup

void loop () { }

Output:

20864

http://www.gammon.com.au/forum/?id=12146

I think the C++ standard covers that:

5.4. If during the evaluation of an expression, the result is not mathematically defined or not in the range of
representable values for its type, the behavior is undefined.

Thus your correct answer above was obtained from undefined behaviour. You can’t rely on it.

Thank you Arrch and Nick for that. That makes sense now.