Just study this page and you'll be enlightened (again) ![]()
It’s not ‘causing’ a negative value, your code is creating it.
Whether that’s right, or not what you’re expecting is entirely up to how you store and process that value.
SIGNED, simply means it’s ’default’ when being processed will possibly include a ‘minus’ sign… if the most significant ‘bit’ is a ‘1’
00000001 == 1 (no sign plus 1)
10000001 == -1 if signed ( - ) 1
10000001 == +128 if unsigned. (127 plus 1)
10000000 == -0 signed ( - ) 0
00000000 == +0 unsigned
e.g. An 8-bit byte can only ever have 256 values, but depending on the ms bit (the sign bit), that range could be 0 to 127, or 0 to -128… (take note)
If it’s UNSIGNED, the msbit is ‘used’ as a part of the numeric value, hence the range is 0 to 255 (-128 to +127)… displayed without any negative offset… (0 to 255)
An 8 or 16, or32 bit value can be positive or negative (in decimal notation), but is irrelevant in hex or binary notation. it’s not hard, but it is tricky until you understand numeric values aren’t stored the way you see them.
The cpu itself has no idea what number-base you’re using, that’s the compiler’s job.
Yes my code is creating it because I'm asking the code to see when a value goes above zero before it does something. So until it goes above zero its calculating negative numbers which appear as huge positive numbers.
Then the question is how will the if statement work if it sees what appears to be a positive number like 4294966796 but that really represent -500.
So my approach now is to work on a scheme that will always keep the numbers positive until the value is reduced to be <=0. But unless its actually 0 I'm not to confident that will work either.
It's perfectly fine to compare two absolute timestamps, as long as you know how to interpret the result. unsigned longs that are greater than MAX_LONG are 2's compliment negative numbers in a signed sense, so you instead of testing less than zero, you could test if they are > MAX_LONG
Thanks I was looking for some way to test for a flip from positive to negative. Hopefully this will work. I believe you helped me out with the code when I had it in the PWM loop which is working perfectly. I didn't understand what was going on in that section of the code but it worked for that. But now its time to figure it out.
Take a look at the "What if I really need to compare timestamps" section of programming - How can I handle the millis() rollover? - Arduino Stack Exchange -- it has a couple good suggestions.
No, that's not right. Arduino, like most modern computer systems, uses 2's complement for signed numbers. Think of the high bit as having the value -128 (rather than +128 for unsigned numbers), and the rest of the logic follows: 10000000 = -128, 10000001 = -127, 11111111 = -1, etc.
I have a lot of reading and testing to do so this will be a slow process. The link you sent looks pretty good. So far I now understand why these variable are declared as unsigned longs. Its because that's what millis returns. What I don't understand is why there is this (signed long) in the if statement. I do know I get different results when I take it out.
if ((signed long)(CurrentMillis - NextBlinkOn) >= 0) {
I find very few references to it on the web and the only one I did find is way over my head.
Yes! You are right when the number representation system is "Sign-Magnitude". In this scheme, the MSBit represents just the sign (1 means -ve number) of the number which is made up by adding the positional vales of the remaining bits.
UNO uses 2's complemet scheme. Here, the MSBit carries negative postional value andd the remaining bits carry positive positional values. To get the maginiture of the number, all the positional values have to be added together.
Thus, 10000001 stands for -128 + 1 = -127.
This is "casting" or a "cast" that says how to interpret the data.
https://en.cppreference.com/w/cpp/language/explicit_cast
https://en.cppreference.com/w/c/language/cast
Yes it will, its perfectly valid to cast to signed before this comparison - in fact you have to do it this way if comparing two unsigned values in a wrap-around-safe manner, as all unsigned values are >= 0.
That is kind of what I figured. But is the casting being performed too late? If the evaluation is performed before the cast it will come out as a very large positive number like 4######### etc. which will then be re cast to the same positive number as a signed long. The if statement will then always evaluate it as being a positive number and > 0 for all cases.
I was thinking of making those variables all signed long and then re-casting the millis statement to something like. CurrentMillis = signed long)(millis()
Or maybe even something like this if((signed long)(CurrentMillis) - NextBlinkOn) >= 0)
Not sure of the correct syntax for the cast but something like this is what I was intending to experiment with today.
- Use unsigned long for time variables.
- Use unsigned byte or unsigned int or unsigned long for required time duration
- Place current time or elapsed time on the left and time duration on the right side of the comparison operators.
Then there'll be no rollover issue.
if (currentMillis - previousMillis) >= NextBlinkOn {
// your code
}
EDIT: see reply #59 for update.
is a proper syntax. First the unsigned longs are subtracted resulting in large unsigned longs before the appointed time, zero at the appointed time, and small unsigned longs after the appointed time. Then cast as a signed long occurs before the comparison, so then the compiler casts the RHS 0 signed
int up to a signed long and does the comparison.
For your original problem here:
You could cast the value printed from an unsigned long to a signed long:
Serial.print((signed long)(CurrentMillis - NextBlinkOn));
and it should print as you expect.
This however is a potential problem near millis() = ULONG_MAX:
... because near the rollover value, millis() + PRINT_INTERVAL can rollover to a small number and then cause the conditional to trigger every time until millis() rolls over as well. The other potential problem is that if this routine happens to be called longer than PRINT_INTERVAL apart, one iteration could set print_timeout to something near ULONG_MAX and if before the next iteration millis() rolls over, it won't trigger the conditional.
I often configure periodic report functions like this:
void report(void){
const int interval = 15000;
static unsigned long last = - interval;
unsigned long now = millis();
if( now - last > interval) {
last += interval;
Serial.print(now);
//...
}
}
I use the static unsigned long last = -interval to roll over backwards and schedule a report() immediately at the beginning of the run when millis() == 0, rather than waiting until the end of the first interval.
A signed long has half the range as an unsigned long in its positive side, so it can only go up to 231-1 (2147483647). A large positive number like 4######### will "wrap around" to a smaller negative number when it's converted to signed long. The wrap-around can be computed manually by subtracting 232 (4294967296) from the number, so 4294966796 (as an unsigned long) is converted to -500 (as a signed long).
Best option is to forget about casting and use the robust method that dlloyd mentioned in #34.
This will take me a while to figure out, but I will get there.
I believe I have tried that, but will try again just to be sure. One of the major difficulties I am having is that when the Serial printer displays a number like 4#########. I'm not sure how the program is interpreting it. So there may be logic issues in the code that I can understand what I need to fix.
The rest of the program seems to work fine, So I'm doing to move forward by just creating this subroutine a stand alone program and work with that. This all works in my previous program where I had all these statements in mingled in with the PWM code. So I think the problem really boils gown to that I'm feeding it garbage and getting garbage out.
The key for me at the moment is to get the serial printer to print exactly what the if statement is evaluating. Because right now it printing out all positive numbers no matter what I do.
Is it just the printing that is broken? Or is there other misbehavior?
On printing, the code is interpreting the result of the subtraction of the two unsigned longs as an unsigned long and printing it:
Serial.print("CurrentMillis - NextBlinkOn = ");
The unsigned longs, as used in this if statement with this casting, it is prevented from being a logic error by the casting:
if ((signed long)(CurrentMillis - NextBlinkOn) >= 0) { ...
There is a logic error in the check_status() function that should be fixed if there's a chance you might reuse the code or run it more than 50 days. The problematic part in the in the report function won't manifest until about 50 days of run time (2^32/1000/3600/24), so if that will never bug you, you don't need to fix it.
Is there some behavior you're seeing, besides the Serial.print((unsigned long)(-500)); = 4294966796` that is causing you problems? -- If it ain't broke, don't fix it. Figure out what is broken first.
I have tried so many iterations of things, I'm not sure which is broke. but I have tried signed long as well as unsigned long and it does behave differently. I have also tried it without a cast. but when I do the serial print and get a number like 4######### its difficult to evaluate the logic. Although it does seem that it is going through the loop when it shouldn't be.
As far as going over 50 days this is purely academic for me at this point as I believe I can use the delay method in this part of the code since it it now outside the PWM code. Also this is for an automobile which is limited to about 5 hr on a tank of gas.
The "unsigned byte or unsigned int" part is interesting. It saves a few instruction cycles in copying the interval for the compare and the increment.
If you know your interval is in the sub-unsigned byte or sub-unsigned int range, you can save several more instructions by casting the difference term, since the compiler only copies enough of the source data to do the cast-ed arithmetic.
The Wokwi simulation linked below uses a few different schemes. And the F1-"View Compiled Assembly Code" output is cool. I wish the Arduino IDE had that feature.