This is a pure C++ programming issue, which I cover on my web site.
However, since link-only answers are not allowed here, I reproduce that post below:
On the Arduino (Uno) platform, what do you think will be printed here?
void setup ()
{
Serial.begin (115200);
Serial.println ();
Serial.println (30000 + 30000); // twice 30000
Serial.println (60 * 60 * 24); // seconds in a day
Serial.println (50 / 100 * 1000); // half of 1000
} // end of setup
void loop () { }
Did you guess:
60000
86400
500
Nope!
It prints:
-5536
20864
0
This is because of integer arithmetic. If the compiler can, it treats an numeric literal (like 60) as an int type, which means it has the range -32768 to +32767. This may be unexpected for users of modern compilers because nowadays an int
is usually 32 bits. However on the 8-bit Arduino processors such as the Atmega328P the compiler treats an int as 16 bits wide. The C++ standard permits an int size of at least 16 bits, but you are not guaranteed that it will be larger.
And, arithmetic is done using the type of the largest argument, which means the arithmetic in each case is done as 16-bit arithmetic, and thus it overflows once it reaches 32767.
For example, 30000 + 30000 = 60000 which is 0xEA60 in hex. Unfortunately, 0xEA60 is exactly how -5536 is stored in an int type, which is why it prints -5536.
Meanwhile, 60 * 60 * 24 = 86400 which is 0x15180 in hex. As that doesn't fit in 16 bits, it is truncated to 0x5180 which is 20864 in decimal (as printed).
Finally, in integer arithmetic 50/100 is zero, multiply zero by 1000 and you still get zero, which is why the final result is zero.
So, can we "help" the compiler by telling it the sort of result we want, like this?
void setup ()
{
Serial.begin (115200);
Serial.println ();
long a = 30000 + 30000;
long b = 60 * 60 * 24;
float c = 50 / 100 * 1000;
Serial.println (a);
Serial.println (b);
Serial.println (c);
} // end of setup
void loop () { }
That prints:
-5536
20864
0.00
So no, that hasn't helped.
Solution
First, you can add a suffix to numeric literals (eg. L for long, or UL for unsigned long), and add a decimal place to floats, like this:
void setup ()
{
Serial.begin (115200);
Serial.println ();
Serial.println (30000L + 30000); // twice 30000
Serial.println (60L * 60 * 24); // seconds in a day
Serial.println (50.0 / 100 * 1000); // half of 1000
} // end of setup
void loop () { }
Now we get:
60000
86400
500.00
You only need to help out with the first literal, once the compiler knows we are using longs (or floats) it sticks with them for the expression. *
Or we can "cast" them:
void setup ()
{
Serial.begin (115200);
Serial.println ();
long a = (long) 30000 + 30000;
long b = (long) 60 * 60 * 24;
float c = (float) 50 / 100 * 1000;
Serial.println (a);
Serial.println (b);
Serial.println (c);
} // end of setup
void loop () { }
Results:
60000
86400
500.00
Casting is useful for variables, because you can't just add "L" to the end of a variable name.
An alternative syntax is to use a constructor like this:
void setup ()
{
Serial.begin (115200);
Serial.println ();
long a = long (30000) + 30000;
long b = long (60) * 60 * 24;
float c = float (50) / 100 * 1000;
Serial.println (a);
Serial.println (b);
Serial.println (c);
} // end of setup
void loop () { }
(Same results).
* It's actually somewhat more complex than that as this link explains: Understand integer conversion rules
The compiler "promotes" a value in an expression to match another "higher-ranked" type, under certain circumstances. For example, adding an int and a long will result in the int being promoted to a long (regardless of whether it appears first in the expression or not). However if an int is being added to another int, it will not promote them to a long, even though the result may not fit into an int.
More explanations
I was asked in the comments why 1000 * 60
is not the same as 60000
from the compiler's point of view.
The answer is that a compiler will treat an integer literal as an int (if possible) and an int on this platform is 16 bits.
Thus 1000 (an int) * 60 (another int) are multiplied using 16-bit arithmetic, giving a result that overflows.
However the literal, 60000, cannot be stored as an int and thus the compiler promotes it to long (aka "long int"). Since it has been promoted to a long it is correctly stored, and further operations on 60000 will be done with long arithmetic.
I have seen people code 60000 as 60000L however that is unnecessary. The compiler is not silly enough to make 60000 be stored as -5536, as that is clearly not the intent of the programmer.