When I did that I got back a result for snooze_delay of 4294962816.
After reading the manual page on long (and bouncing around several others), I changed it to:
snooze_delay = 60 * 60 * 1000L;
And it worked. Currently running this on an Arduino Nano.
Just wanted to see if I understood it right:
Whenever Arduino sees a number in code, it assumes it's an int.
So in my initial expression, snooze_delay is a container that can store an unsigned long value. Which is just fine.
The problem is that when I type in 60 x 60 x 1000, Arduino understands all 3 numbers as int, and allocates resources for int, and the result is classified as int automatically.
The assignment occurs after the multiplication, so in the first part, Arduino does 60x60x1000, which is greater than the 32767 int limit, and the result is corrupted.
The corrupted result gets assigned to my snooze_delay container, at that point any negative sign is stripped and snooze_delay ends up with an incorrect value.
When I say "1000L" in the second case, Arduino allocates resources for a long value to 1000, and those resources are big enough to hold the resulting multiplication, so nothing goes wrong. Then the result, which is long, gets put into snooze_delay and that's all she wrote.
I assume that if I were using a variable in my multiplication (instead of just numbers), and that variable was of long type, I wouldn't need the "L" since already one of the values would have been assigned long resources.
By naming the constants, the code becomes more readable and everyone knows what the numbers mean. You don't have to guess. Since the expression snooze_delay = 60 * 60 * 1000L; contains only constants, you can also use snooze_delay as a constant wie z.B. constexpr unsigned long SNOOZE_DELAY {HOURS*MINUTES*SECONDS};
You were lucky though because 60 * 60 does fit within 2 bytes
If you had written
snooze_delay = 1000 * 60 * 60L;
You likely (can’t test right away - but I highly suspect) would still have the wrong result because 60x1000 does not fit in an int.
The best practice is to tag all the numbers to be on the safe side or understand the evaluations order rules and at least the first one in every sub expression (if you use parentheses). If you write
snooze_delay = 1000L * 60 * 60;
Then the first multiplication leads to a long and thus the second multiplication being between a long and an int is also carried out as a long
Last but not least - you should use UL (or ul) because what you really want is an unsigned long result so I would recommend
snooze_delay = 1000ul * 60ul * 60ul;
Or use typed constants. The compiler will do the math and get rid of the variables you don’t use post static maths evaluation so it won’t take more memory and is more readable
const unsigned long oneSecond_ms = 1000ul;
const unsigned long oneMinute_ms = oneSecond_ms * 60ul;
const unsigned long oneHour_ms = oneMinute_ms * 60ul;
(You can also use constexpr instead of const to be sure of compile time evaluations)
Side note
Not completely. The number is assigned the smaller type that can represent the number so if you write
long x = 186000;
The number 186000 won’t be assumed to be an int and lead to a wrong assignment to x
So if you do
long d1 = 3600000 * 24;
long d2 = 60l * 60l * 1000l * 24l;
Serial.println(d1);
Serial.println(d2);
d1 and d2 will have the same value because the multiplication for d1 will have been done with a long since 3600000 was not fitting in an int
So... why did you "byte" for MINUTE and SECOND? Shouldn't all values associated with times be "unsigned long" ?
It's an argument I have against the "formalization" of programming.
"Writing 1000 is incorrect. What you really need is constexpr uint_least32_t milliSecondsPerSecond {1000};, because that's much more readable!" Right.
I have chosen byte for minutes and hours, because the necessary value range is < than 255 and never negative.
Because SECONDS is defined with unsigned long or uint_least32_t, the expression SECONDS * MINUTES * HOURS will bring the correct result in every combination of the factors.
So why use eight bytes for two numbers when only two bytes are needed?
With the designation of SECONDS and milliSecondsPerSecond I agree with you completely. That is better.
That’s the theory though. Byte are promoted to int when you do maths for example (or unsigned int if the rest of the expression is unsigned) so unless you explicitly cast again your variable to byte when using it, more bytes will be really involved.
Also the optimizer will likely evaluate most of the math formula that can be evaluated at compile time so it’s possible that your constexpr will never exist really in memory.