Aw sweet, it all makes sense now. I executed the following code
unsigned long var;
unsigned long varminus;
void setup(void)
{
// initialize serial:
Serial.begin(9600);
var = 100;
varminus = var-200;
Serial.println("The answer to 100 minus 200 on unsigned-long vars is: ");
Serial.println(varminus);
}
void loop(void)
{
}
And the output is:
The answer to 100 minus 200 on unsigned-long vars is:
4294967196
The odometer analogy is the perfect one for a commoner to get around obscure binary/hex-thinking.
In any case, many thanks for forum members that stuck around, I've edited OP to accommodate future reference
While a staunch defender of the "pure" English tongue, on cogitation, I do believe the term "modular arithmetic" is in fact quite correct and indeed, more accurately descriptive than "modulo arithmetic".
The modulus is a parameter of a numeric value determined by a specific operation and the act of performing that operation is described as "modulo" or a modulo function. So the arithmetic of performing further operations - notably comparisons - on such results, the moduli as a plural, can appropriately be described as "modular" arithmetic where "modular" derives from the "moduli" in the same way we refer to "alveolar" where the (lung) alveoli are concerned, and a few other Latin terms - though I cannot quote the original Latin rules behind that (I never formally studied Latin).
There could be claimed to be the occasional word ending in o - such as "medico" whose apparent derived adjective ("medical") ends in "al" but I suspect this is spurious and results from the colloquial addition of "o" to a word (such as "nymph") whose derived adjective did end in "al".
My point - for what it is worth - is that "modulo" is the name of a process the result of which is the modulus and the arithmetic relating to these is concerned more with what you can do with the moduli, than how you obtain them.
The solution that I used to avoid the rollover issue was to make my own replacement for millis() that returns a "unsigned long long int". It calls millis() to get the current time and remembers the last value that it got from millis(), so if the current time is less than the previous time, it adds 0x0100000000UL to the previous time and then subtracts the current time (this getting the elapsed time) and then adds the elapsed time to the "unsigned long long int" variable that it uses to keep track of the current time. There is still a possibility of a rollover, but it is pushed so far into the future that I don't consider it an issue. The rollover would occur around the 584.9 million year mark. The only issue with this method is that if successive calls to this routine do not happen at least once every millis() rollover interval, it could miss a rollover and the time it keeps track of might not be accurate. Since I use this routine for scheduling automatic actions in a state machine instead of using delay() calls, the routine is called every time the loop() routine is called. As such, I don't foresee there will be a case where the code is not called for a long enough period of time that the millis() rollover would be an issue.
Robin2:
That sounds awfully complicated when the rollover problem can be avoided simply by using subtraction as in
if (millis() - previousTime >= desiredInterval) {
...R
I suspect that it's more complicated describing it than actually doing it.
My system consists of basically a scheduler and events that can occur. There might be an event for turning on a particular pin in the future and another one for turning it off at some point later. The event handler might generate future events in the handling of the current event that it is processing. It is possible that it might be necessary to schedule an event that will happen after multiple millis() rollovers. Each of the events in the event queue have a 64-bit time value associated with them to tell the event handler when it needs to process that event.
Manipulating 64-bit integers is inefficient with these processors, but for what I use it for, it's fast enough. The events that are being scheduled are a few seconds apart at the closest and often hours or days apart -- some might even be months apart. Having the capability to schedule events that are across multiple millis() rollover intervals was more important to me than a few extra processor cycles being spend on 64-bit arithmetic. I will admit though that having a 584.9M year rollover is a bit more than I needed.
Just did a quick timing test, printing out the elapsed time between successive calls to GetTime() in my code and it works out to be 4ms, with the occasional 5ms time. Trying the same thing with a loop of calling millis() and displaying the elapsed time using "unsigned long int" values results in mostly 3ms with the occasional 4ms value. It's possible that the Serial.println() call is taking up a good portion of that.
So, I decided to try another test without the console output... I used a loop of 10,000 where millis() was called, stored as the current time, calculate the elapsed time, and then store the current time as the previous time. This results in 15ms for the 10,000 iterations of the loop.
Changing this to use the 64-bit unsigned integer in my GetTime() routine results in 187ms for the 10,000 iterations of the loop. So, it most definitely takes a lot longer to do 64-bit math. For some systems, this might be an unacceptable time penalty. For other systems though, this might be perfectly acceptable.
typedef unsigned long long int TIME64;
TIME64 ct; /* current time */
TIME64 pt = 0; /* previous time */
TIME64 et; /* elapsed time */
unsigned long startTime;
unsigned long endTime;
int i;
Serial.println("Testing millis() loop...");
startTime = millis();
for (i = 0; i < 10000; i++) {
ct = GetTime();
et = ct - pt;
// Serial.println(et);
pt = ct;
}
endTime = millis();
Serial.print("startTime=");
Serial.print(startTime);
Serial.print(", endTime=");
Serial.print(endTime);
Serial.print(", elapsedTime=");
Serial.println(endTime-startTime);
NavyVet1959:
The event handler might generate future events in the handling of the current event that it is processing. It is possible that it might be necessary to schedule an event that will happen after multiple millis() rollovers.
That sounds like a role for a Real Time Clock
Alternatively why not use millis() to update a 1-second counter in an unsigned-long variable. That should see you out.
I haven't had the pleasure to have that error yet, but I've done a lot of 'C' programming in non-memory-protected architectures over the years and I've had my share of fun with strange errors that were hard to trace down. Ones where you put a printf statement in the code to see the value of some variable at some point and when you add the code, the program either starts working correctly or the error gets moved elsewhere. Of course, you know you are overwriting your code somewhere, but trying to find it can be a pain.
Is there something about the "long long" data types that might cause this sort of error to occur more often?
NavyVet1959:
Is there something about the "long long" data types that might cause this sort of error to occur more often?
Datatype size, the amount of data that can be stored in processor registers, and a likely compiler limitation that prevents a stack frame from being created when too much data is being squeezed into too small of a register space.