I noticed today that when I run this sketch:
// variables:
unsigned long variableA = 60 * 60 * 12;
unsigned long variableB = 60 * 60 * 12L;
unsigned long variableC = 43199 + 1;
unsigned long variableD = 21600 * 2;
unsigned long variableE = 21600 + 21600;
void setup()
{
// start the serial:
Serial.begin(9600);
// print the variables:
Serial.print("variableA: ");
Serial.println(variableA);
Serial.print("variableB: ");
Serial.println(variableB);
Serial.print("variableC: ");
Serial.println(variableC);
Serial.print("variableD: ");
Serial.println(variableD);
Serial.print("variableE: ");
Serial.println(variableE);
}
void loop() {
}
I get the following from serial:
variableA: 4294944960
variableB: 43200
variableC: 43200
variableD: 4294944960
variableE: 4294944960
So even though I am declaring all of the above variables as unsigned long , which is good for storing a number up to 4,294,967,295 , it seems there needs to be at least one LONG number within the arithmetic, or one designator: "L" in order to get the proper result.
Arduino explains that numbers within a sketch (Integer Constants) are treated as Int's,
but that still doesn't explain to me why:
1. A long number can apparently exist as an integer constant without a designation "L". (see variableC in code above)
2. A designation "L" is required to get a LONG result from arithmetic that uses "int" integer constants. (see variableB in code above)
Can someone explain why this is?
If you don't tell the compiler the data type, it assumes a 16 bit int. In the two statements where you omit the long type specifier, the compiler does a silent cast to a 32 bit number after the expression is resolved, which evidently it messes up. If you use the long type specifier in either expression prior to the silent cast, it gets the answer correct. Someone else might look at the assembler and give you a better explanation.
I modified your program to show how silent casts are nettlesome problems.
// variables:
unsigned long exp1 = 60;
unsigned long exp2 = 12;
unsigned long exp3 = 43199;
unsigned long exp4 = 2;
unsigned long exp5 = 21600;
unsigned long variableA = 60 * 60 * 12;
unsigned long variableB = 60 * 60 * 12L;
unsigned long variableC = 43199 + 1;
unsigned long variableD = 21600 * 2;
unsigned long variableE = 21600 + 21600;
void setup()
{
// start the serial:
Serial.begin(115200);
// print the variables:
Serial.print("variableA: ");
Serial.println(variableA);
Serial.print("variableB: ");
Serial.println(variableB);
Serial.print("variableC: ");
Serial.println(variableC);
Serial.print("variableD: ");
Serial.println(variableD);
Serial.print("variableE: ");
Serial.println(variableE);
Serial.println("================== No silent casts");
variableA = exp1 * exp1 * exp2;
variableB = exp1 * exp1 * 12L;
variableC = exp3 + 1;
variableD = exp5 * 2L;
variableE = exp5 + 21600L;
Serial.print("variableA: ");
Serial.println(variableA);
Serial.print("variableB: ");
Serial.println(variableB);
Serial.print("variableC: ");
Serial.println(variableC);
Serial.print("variableD: ");
Serial.println(variableD);
Serial.print("variableE: ");
Serial.println(variableE);
}
void loop() {
}
You can also use 'UL' to force an unsigned long.
Thanks.
Looks Like I should be adding "L" designation to all of the integer constants... not just one.
example:
60 * 60 * 12L = correct value.
60 * 60 * 60 * 12L = wrong value.
60 * 60 * 60L * 12L = correct value.
60L * 60L * 60L * 12L = correct value.
My problem was:
I assumed that the "unsigned long" variable-type definition would have some effect on the arithmetic that is associated with the variable, but nope.
"unsigned long" is just a variable-type, and the math that goes into coming up with the value is all defaulted to int. Seems strange, but I guess that's just how it is.
In the absence of parentheses, evaluation of consecutive multiplies is done left to right and the definition of the C language requires that an integer constant without a length qualifier (e.g. (long) or L) is of type int - which on 328-based Arduinos is 16 bits. The language spec also requires that the intermediate result of multiplying an int by an int is an int (NOT a long) - it doesn't matter what the type of the variable on the left hand side is.
This expression is evaluated left to right.
60 * 60 * 60 * 12L = wrong value.
6060 = 3600
but then
360060 is NOT 216000 because it doesn't fit - I think it results in 19392 which is then multiplied by 12L which gives a long result of 232704.
unsigned long variableA = 60 * 60 * 12
In this case we have 6060 = 3600 as before. But now we do an intint of 3600*12 which results in 43200. Although 43200 fits in 16-bits it doesn't fit in a (signed) int, it has the sign bit set. When this number is converted to unsigned long, the sign bit is propagated up to the top of the 32-bit word. So what started as 43200 (which in hex is A8C0) is extended to FFFFA8C0 giving an unsigned long 4294944960.
Evidently when a signed int is converted to an unsigned long, the first step is to do the sign extension and then transfer the result rather than to treat A8C0 as unsigned first and then store it in an unsigned long.
Pete
Thanks Nick.
I love your website, guess I missed that part in the past.
:o