Subtracting unsigned long

Hi.

The code:

  if ( (anycast?) (longNow-longLast) < 0 ) {  

  }

… requires any kind of casting?

Thank you
H. Martins

unsigned long - unsigned long = unsigned long
Cannot be < 0

This code will not work as intended, since the result of unsigned subtraction is still unsigned and thus cannot be negative.

You don’t need casting. You need to abandon the habit of using subtraction for comparison purposes. This is not a good practice, regardless of whether you are dealing with signed or unsigned types.

Just use

if (longNow < longLast) 
{  
}

and it will work properly regardless of the type.


But if for some reason you insist on using subraction, you have to perform the cast to signed type before subtraction, not after it. And you have to make sure that both operands have signed type

if ((long) longNow - (long) longLast < 0) 
{  
}

Remember to watch for potential overflow (which is the reason why subtraction is not a good idea).

Remember to watch for potential overflow (which is the reason why subtraction is not a good idea).

Wrong - subtraction with unsigned int is used to avoid problems with rollover take a look at the blink without delay example.

Mark

holmes4:
Wrong - subtraction with unsigned int is used to avoid problems with rollover take a look at the blink without delay example.

I stated clearly and explicitly that the above applies specifically in the context of using subtraction for comparison purposes, i.e. to determine which one of the two values is greater: a < b, a > b or a == b. The BlinkWithoutDelay example you mention has nothing to do with this matter.

(Or, more precisely, BlinkWithoutDelay does compare some unsigned values and it does it exactly how I said it should be done.)

So, no, not “wrong”, but very very right.

Montmorency:
I stated clearly and explicitly that the above applies specifically in the context of using subtraction for comparison purposes,

So, no, not “wrong”, but very very right.

Well if (millis() - previousMillis < 1000) {...} // or > is using subtraction of unsigned longs for comparison purpose and it does handle rollover correctly - so your statement is at best incomplete

Reality is

  • the result will never be negative unless you cast indeed to a signed version of the type and that it fits, which is a risk when casting from unsigned long to long
  • that doing if (x < y) is indeed the way to go in general
  • that rollover will still play tricks on you with as mathematically x can be less than y whilst in reality representing a greater conceptual value (as in time passing by - Y2K issue for example)

J-M-L:
Well if (millis() - previousMillis < 1000) {...} // or > is using subtraction of unsigned longs for comparison purpose and it does handle rollover correctly - so your statement is at best incomplete

No, this code does NOT use subtraction to implement comparison. The - between millis() between previousMillis is not used for relational comparison between these values.

What this code IS trying to subject to relational comparison is the value of millis() - previousMillis(value a) and the value 1000 (value b).

According to what I said, the proper way to compare these values is to write a < b, a > b, a <= b, a == b and so on, depending on authors intent. And that is exactly what the above code does.

However, some authors prefer to use subtraction to compare a and b. They insist on implementing these comparisons as a - b < 0, a - b > 0, a - b == 0 and so on. This technique “works” sometimes, but it in many cases it will break down. And with unsigned types it will never work.

If the above example was “using subtraction for comparison purposes”, it would look as follows

if ((millis() - previousMillis) - 1000 < 0) {...}

See the difference?

This version is very bad. Fortunately, it is not written like that.

So, just because you saw a - operator somewhere in the middle of an expression inside an if condition, it does not mean that that - is employed to implement relational comparison. Sometimes a subtraction is just a subtraction.

Montmorency:
What this code IS trying to subject to relational comparison is the value of millis() - previousMillis(value a) and the value 1000 (value b).

So with your Theory if a is millis() and b is (previousMillis + 1000), One could argue then that you are suggesting to do millis() < (previousMillis + 1000)… and everyone knows this is a bad idea.

that’s why I’m stating “your statement is at best incomplete

Montmorency: You don't need casting. You need to abandon the habit of using subtraction for comparison purposes. This is not a good practice, regardless of whether you are dealing with signed or unsigned types.

In arithmetic, we have subtraction process; when we do it on paper, we see that both numbers remain intact and there is another 3rd number which we call result.

Looking at the result, we decide the relation between the given two numbers as to which one is bigger; which one is smaller; or they are equal.

Computers are made by human beings. Is it possible to have a computer that can do comparison on two numbers without doing subtraction on them?

GolamMostafa: Computers are made by human beings. Is it possible to have a computer that can do comparison on two numbers without doing subtraction on them?

of course for unsigned integers --> an XOR between the 2 binary representation tells you they are equals if the result is 0

comparing bits starting from the most signficant bit will tell you which number is the largest (the value with a 1 when the other is 0 is greater)

Montmorency: No, this code does NOT use subtraction to implement comparison.

The dictionary, this community, and anyone who made it through junior high math disagree. Move on.

The next post will directly address the original poster or be written by the original poster.

J-M-L:
So with your Theory if a is millis() and b is (previousMillis + 1000), One could argue then that you are suggesting to do millis() < (previousMillis + 1000)… and everyone knows this is a bad idea.

I cannot leave this unaddressed since this is a patently wrong statement that is damaging to the common knowledge. You said that “everyone knows this is a bad idea”

Apparently, you read the rationale behind your “good” version and assumed that you “got it”. But in reality you never understood it well enough to draw proper conclusions from that, as evidenced by your incorrect claim above.

The above post of mine was incorrect. I retract it.

@Montmorency - your understanding of the intricacies of C++ are better than your understanding of simple English

The next post will directly address the original poster or be written by the original poster.

You need to work on that.

HMartins:

  if ( (anycast?) (longNow-longLast) < 0 ) {  

}

A computer is a technical machine; if it gets ‘technically valid data’, it guarantees that it will give you back ‘technically valid’ result. However, a computer has a serious limitation that it can not validate the ‘technical correctness’ of all the received data. That’s why the human beings are here; they first justify the ‘technical validity’ of the data before the data are placed to the computer for processing.

In your above quote, you have asked the UNO to perform the subtraction operation on two unsigned long variables and then checking if the result is less than 0. The variables are:

unsigned long longNow and
unsigned long longLast.

For the sake of debate, I would like to justify that the process (longNow-longLast<0) that you have asked the UNO to resolve is ‘technically invalid’.

Let us assign present elapsed time of the UNO to longLast variable:

unsiged long longLast = millis();

Let us assign current elapsed time of the UNO to longNow variable:

unsigned longNow = millis();

Which one bigger – longNow or longLast?

If longNow is bigger (it should be bigger as time can not go backward), how can the result of this operation: longNow - longLast be less than 0. So, the operation that you have asked the UNO to do is ‘technically invalid’.

Try to solve the following operations using UNO and see what result you get on the Serial Monitor; whereas, on paper we can do it easily.

unsigned int x1 = 0xF000;  //61440
unsigned int x2 = 0x1234;  //4660
Serial.print(x2 - x1);          //shows ?

On paper, we find: 4660 - 61440= - 56780; but, the UNO can’t; this is the limitation of a computer; a computer does not posses an abstract faculty like human beings.

Montmorency:
You just offented everyone’s extertise in this forum. This version is perfectly good!

If variable previousMillis is propely declared as unsigned long, then the above version is absolutly correct. Theres’ nothing “bad” about it, contrary to your baseless claim. If it pefectly 100% equivalent to the

(millis() - previousMillis) < 1000

version. Both versions will correctly take advantage of rollover poroperties of unsigned long type to hide a single rollover of millis(). You can use either one, depending on your personal preference.

Sorry AWOL, sorry OP. I feel this needs to be addressed…

@Montmorency

  • assume previousMillis is indeed an unsigned long. Rollover is thus at 4,294,967,295
  • imagine we set previousMillis at 4,294,967,000

you want your code to do something for 1 second, 1000 ms before doing something else and according to your explanation you write

if (millis() < (previousMillis + 1000)) {
 // do something GOOD during that one second
} else {
  // now 1 second has elapsed, lets do something else
  previousMillis = millis();
}

time passes by and millis() is now 4,294,967,001 - so 1 millisecond later.

Let’s look at the if test:

  • millis() is 4,294,967,001
  • due to rollover, previousMillis + 1000 is 4,294,967,000 + 1,000 = 704

so you compare if (4294967001 < 704) {...

This test is not true, so you go to the else part and you have not done something GOOD for 1 second, just barely a millisecond.

Now assume you wrote that as

if (millis() - previousMillis < 1000) {
 // do something GOOD during that one second
} else {
  // now 1 second has elapsed, lets do something else
  previousMillis = millis();
}

millis() - previousMillis will be 4,294,967,001 - 4,294,967,000 = 1 and the test becomes

if (1 < 1000) {... → now you do the right thing

So I disagree with your statement:

Apparently, you read the rationale behind your “good” version and assumed that you “got it”. But in reality you never understood it well enough to draw proper conclusions from that, as evidenced by your incorrect claim above.

and Apparently you are full of certitudes…

J-M-L: Sorry AWOL, sorry OP. I feel this needs to be addressed...

@Montmorency

I stand corrected. Indeed, my previous message was written in haste. What I said there about the equivalence of these two methods is absolutely incorrect.

Montmorency:
Just use

if (longNow < longLast) 


}




and it will work properly regardless of the type.

Thank you. It is indeed a solution, obvious … after you have told me :slight_smile:

Thank you.
H. Martins

GolamMostafa:
A computer is a technical machine; if it gets ‘technically valid data’, it guarantees that it will give you back ‘technically valid’ result.

Exactly.

It is important to remember though that we are talking not about “computer”, but rather about a higher level concept: a programming language. About C++ specifically. The C++ language is defined by a very serious document: the language standard. The standard precisely defines what shall and what shall not happen in language constructs. And if the language standard decides to leave something undefined - it explicitly says so.

The language’s behavior is defined by its standard, not by someone’s arbitrary concept of what constitutes “technically valid data”.

GolamMostafa:
In your above quote, you have asked the UNO to perform the subtraction operation on two unsigned long variables and then checking if the result is less than 0.

Let us assign present elapsed time of the UNO to longLast variable:

unsiged long longLast = millis();

Let us assign current elapsed time of the UNO to longNow variable:

unsigned longNow = millis();

Which one bigger – longNow or longLast?

If longNow is bigger (it should be bigger as time can not go backward), how can the result of this operation: longNow - longLast be less than 0. So, the operation that you have asked the UNO to do is ‘technically invalid’.

And this is where you start replacing the well and strictly defined standard concepts of C++ language with your own “inventions” that has no relevance to C++ at all.

The specification of C and C++ languages clearly and explicitly states that unsigned types in C and C++ implement modulo arithmetic. They operate with values in [0, 2<sup>N</sup>) range in full accordance with the rules of modulo arithmetic. There’s no such thing as “less than 0” in modulo arithmetic. And subtracting larger value from smaller value does NOT produce anything “less than 0”. There’s nothing “technically invalid” about such subtraction in modulo arithmetic world.

This “technically invalid” qualification is a product of your imagination. Everything is perfectly valid here. C++ language specification says so. And that is all that matters.

GolamMostafa:
Try to solve the following operations using UNO and see what result you get on the Serial Monitor; whereas, on paper we can do it easily.

unsigned int x1 = 0xF000;  //61440

unsigned int x2 = 0x1234;  //4660
Serial.print(x2 - x1);          //shows ?




On paper, we find: 4660 - 61440= - 56780; but, the UNO can't; this is the limitation of a computer; a computer does not posses an abstract faculty like human beings.

That is, again, completely incorrect analysis, rooted in your completely unfounded expectations.

On AVR platform unsigned int is a 16-bit unsigned integer type, which means that in full accordance with the requirements and guarantees of C++ language (and C as well) it implements modulo arithmetic for modulo 216. The result of x2 - x1 in your example is an unsigned int value 8756, exactly as language specification requires and as every competent C or C++ programmer expects.

The fact that the output is not -56780, it is not “the limitation of a computer” at all. It is nothing more than a disparity between the proper behavior of C++ language and your completely unfounded expectations.

Meanwhile, UNO does exactly what it was required to do by C++ language. It performed flawlessly.