I'm still just coming up to speed on programming in C and have a question?
I'm just curious about how the Adruino C works for math with
unsigned long values.
Say I have two variables. A and B and I wish to subtract B from A to get C.
Is:
C = A - B ;
the same as:
C = A - ~B + 1;
for all corner cases?
Or does it convert the unsigned into signed for the + operation?
All variables a "unsigned long"
and yes, I'm assuming two's complement math.
Dwight
please note that ....
void setup()
{
Serial.begin(115200);
Serial.print("Start ");
Serial.println(__FILE__);
int x = -32768;
Serial.println(x);
x = -x;
Serial.println(x);
}
void loop()
{
}
Why would you assume two's complement as it's unsigned?
And a test (in 4-bit, 32 is so long :p)
15 - 8 = 7
In binary C = A - B:
0b1111 - 0b1000 = 0b0111
And with A - ~B + 1:
~8 => 0b0111
15 - ~8 = 0b1000
15 - ~8 + 1 = 0b1001
So no, its not the same...
Is:
C = A - B ;
the same as:
C = A - ~B + 1;
No. That is not two's complement subtraction. It should be:
C = A + ~B + 1;
The subtraction is the same whether it is signed or unsigned - two's complement
Pete
Yeay, the real two complement works, but that's the idea of the two complement.
But why should you? It's unsigned? It's just binary..
Sorry, typo on my part.
SB
C = A - B ;
the same as
C = A + ~B + 1 ;
What I'm asking is what is - operator to unsigned values?
Dwight
It's not that hard to test is it?
And with A + ~B + 1:
15 => 0b1111
~8 => 0b0111
15 + ~8 = 0b10110 => 0b0110 (in 4-bit, role over)
15 + ~8 + 1 = 0b0111
For 32-bit (aka long) it's the same but longer....
septillion:
It's not that hard to test is it?
And with A + ~B + 1:
15 => 0b1111
~8 => 0b0111
15 + ~8 = 0b10110 => 0b0110 (in 4-bit, role over)
15 + ~8 + 1 = 0b0111
For 32-bit (aka long) it's the same but longer....
That isn't testing the corner cases.
Things like:
2147483649 - 2147483647
or
3 - 4294967293
I guess I'll have to test it to really know for sure. The concept of
subtraction doesn't really exit in all cases for an unsigned value.
There is not concept of less than zero.
If the processor is always doing "A + ~B + 1", even for unsigned value, that is fine
but if it is handling the sign differently it could be an issue. ( not sure how it
could be different ).
That is why I asked.
Dwight
dwightthinker:
There is not concept of less than zero.
Because it is unsigned. So negative results are undefined. What else would you expect?
The processor just does a two's complement subtraction.
The value 0xFFFFFFFFUL is 4294967295.
If you subtract one from it you get 0xFFFFFFFEUL which is 4294967294.
If you add one to it, you get zero because you've overflowed the 32-bit value.
The value 0xFFFFFFFFL is -1.
If you subtract one from it, you get 0xFFFFFFFEL which -2.
If you add one to it, you get zero because -1 + 1 = 0
The subtractions give the same bit pattern and the additions have the same bit pattern. The difference is in how you interpret the result.
Pete
I now have two different answers.
"The processor just does a two's complement subtraction."
and
"So negative results are undefined."
I will restate my question.
I expect overflow and require it in fact.
should I use:
C = A - B;
or be safe with:
C = A + ~B + 1;
I know exactly what to expect from the second.
I'm just not sure about the first.
Dwight
It will just roll over. So in 8-bit (but 32-bit will do the same) 0xFF + 1 will just give you 0.
Yep, but unless the compiler optimizes the second, the first is quicker...
Something I do sometimes when I need a for loop to count back and include 0:
for(byte i = 5; 1 < 255; i--){
After 0 it will go to 255 and stop 
dwightthinker:
Is:
C = A - B ;
the same as:
C = A - ~B + 1; // I think you mean A + ~B + 1, whatever
for all corner cases?
Or does it convert the unsigned into signed for the + operation?
You are interested in C++ value promotion (is what they call it).
The main corner case is promoting a signed int to unsigned long. The question is whether the value will be treated as an unsigned quantity when it is sized up. That is, does this:
((int) -1) + ((unsigned long)0)
come out as 0x0000FFFF or as 0xFFFFFFFF ?
According to this page Implicit conversions - cppreference.com , sign extension gets done first. This means that
(long) (unsigned long) (int) -1;
comes out as -1L, which realistically is usually what you want.
There's no need to specify you are assuming twos-complement arithmetic: it's part of the C language that signed integer quantities are twos-complement.
dwightthinker:
Or does it convert the unsigned into signed for the + operation?
The beauty of twos-complement arithmentic is that this doesn't matter. That's why chips and languages use it. the bit patterns resulting from signed and unsigned additions and subtractions are the same - it's just a matter of how you chose to interpret them.
Thanks, I just wasn't sure what the compiler would do. You'll notice
that in my code, I got the + right and it was a typo when posting.
I slightly dyslexic and tend to make a lot of these types of errors.
I usually have to reread and even then edit post to fix such errors.
I'll most likely go back through my code and patch the - back in.
Dwight