24

There are two unsigned ints (x and y) that need to be subtracted. x is always larger than y. However, both x and y can wrap around; for example, if they were both bytes, after 0xff comes 0x00. The problem case is if x wraps around, while y does not. Now x appears to be smaller than y. Luckily, x will not wrap around twice (only once is guaranteed). Assuming bytes, x has wrapped and is now 0x2, whereas y has not and is 0xFE. The right answer of x - y is supposed to be 0x4.

Maybe,

( x > y) ? (x-y) : (x+0xff-y);

But I think there is another way, something involving 2s compliment?, and in this embedded system, x and y are the largest unsigned int types, so adding 0xff... is not possible

What is the best way to write the statement (target language is C)?

mgag
  • 919
  • 2
  • 8
  • 13
  • What you except from this "best way" ? Please clarify your requirements ... – ThinkJet Jan 14 '10 at 00:20
  • What do you mean "both x and y can wrap around"? Do you mean that they might wrap around if cast from unsigned to signed types? – jamesdlin Jan 14 '10 at 00:26
  • The two ints are unsigned. The problem case is if x wraps around, while y does not. Now x appears to be smaller than y. Luckily, x will not wrap around twice (only once is guaranteed). Assuming bytes, x has wrapped and is now 0x2, whereas y has not and is 0x14. The right answer of x - y is supposed to be 0x4. ( x > y) ? (x-y) : (x+0xff-y); but I think there is another way, and in this embedded system, x and y are the largest unsigned int types, so adding 0xff... is not possible. – mgag Jan 14 '10 at 00:47
  • If a *signed* integral value has "wrapped around" (overflown), all bets are off. If you're on a two's complement system, then you can still subtract them assign to an `unsigned` value, but it's not portable. Garbage in, garbage out. – Alok Singhal Jan 14 '10 at 00:53
  • 1
    Your reformulated question still makes little sense. Value don't wrap around by themselves. Once you have `x` that `x` will not wrap anywhere until you start modifying it somehow. If you are modifying it, show us how. At this time there's no way to meaningfully figure out what "wrap around" you are talikng about. – AnT stands with Russia Jan 14 '10 at 01:52
  • Alok: Signed integer overflow is undefined behavior. – Joey Sep 22 '11 at 10:27
  • Also worth to have a look at: [*Is unsigned integer subtraction defined behavior?*](http://stackoverflow.com/q/7221409/1168156) :) – LihO Feb 24 '13 at 16:37

9 Answers9

37

Assuming two unsigned integers:

  • If you know that one is supposed to be "larger" than the other, just subtract. It will work provided you haven't wrapped around more than once (obviously, if you have, you won't be able to tell).
  • If you don't know that one is larger than the other, subtract and cast the result to a signed int of the same width. It will work provided the difference between the two is in the range of the signed int (if not, you won't be able to tell).

To clarify: the scenario described by the original poster seems to be confusing people, but is typical of monotonically increasing fixed-width counters, such as hardware tick counters, or sequence numbers in protocols. The counter goes (e.g. for 8 bits) 0xfc, 0xfd, 0xfe, 0xff, 0x00, 0x01, 0x02, 0x03 etc., and you know that of the two values x and y that you have, x comes later. If x==0x02 and y==0xfe, the calculation x-y (as an 8-bit result) will give the correct answer of 4, assuming that subtraction of two n-bit values wraps modulo 2n - which C99 guarantees for subtraction of unsigned values. (Note: the C standard does not guarantee this behaviour for subtraction of signed values.)

Matthew Slattery
  • 45,290
  • 8
  • 103
  • 119
  • 10
    If you're using an unsigned type in C, it is guaranteed to wrap around as described here, whether or not the system uses 2s complement (§6.2.5, paragraph 9: "A computation involving unsigned operands can never overflow, because a result that cannot be represented by the resulting unsigned integer type is reduced modulo the number that is one greater than the largest value that can be represented by the resulting type.") – Stephen Canon Jan 14 '10 at 01:09
  • 3
    It bears repeating that Stephen is completely correct. unsigned arithmetic is completely defined in C, based on the width of the unsigned type. – caf Jan 14 '10 at 03:22
  • 1
    @Stephen: The OP changed the question, and initially his wording implied that he expected signed values to wrap around predictably. You are, of course, completely correct. – Alok Singhal Jan 14 '10 at 03:52
  • 1
    @endolith: thanks for commenting on this old answer and prodding me into editing it. You and others here are quite right; at the time I answered this, I'd encountered the trick before, but wasn't aware of the guarantee in the standard that it always works for unsigned subtraction. – Matthew Slattery Feb 11 '12 at 00:39
  • 3
    Casting unsigned int to signed int is not safe. §6.3.1.3 Signed and unsigned integers: the new type is signed and the value cannot be represented in it; either the result is implementation-defined or an implementation-defined signal is raised. – czz Apr 11 '13 at 10:51
23

Here's a little more detail of why it 'just works' when you subtract the 'smaller' from the 'larger'.

A couple of things going into this…
1. In hardware, subtraction uses addition: The appropriate operand is simply negated before being added.
2. In two’s complement (which pretty much everything uses), an integer is negated by inverting all the bits then adding 1.

Hardware does this more efficiently than it sounds from the above description, but that’s the basic algorithm for subtraction (even when values are unsigned).

So, lets figure 2 – 250 using 8bit unsigned integers. In binary we have

  0 0 0 0 0 0 1 0  
- 1 1 1 1 1 0 1 0

We negate the operand being subtracted and then add. Recall that to negate we invert all the bits then add 1. After inverting the bits of the second operand we have

0 0 0 0 0 1 0 1  

Then after adding 1 we have

0 0 0 0 0 1 1 0  

Now we perform addition...

  0 0 0 0 0 0 1 0   
+ 0 0 0 0 0 1 1 0

= 0 0 0 0 1 0 0 0 = 8, which is the result we wanted from 2 - 250
A. Sarid
  • 3,916
  • 2
  • 31
  • 56
Purdude
  • 230
  • 2
  • 4
13

Maybe I don't understand, but what's wrong with:

unsigned r = x - y;

GManNickG
  • 494,350
  • 52
  • 494
  • 543
3

The question, as stated, is confusing. You said that you are subtracting unsigned values. If x is always larger than y, as you said, then x - y cannot possibly wrap around or overflow. So you just do x - y (if that's what you need) and that's it.

AnT stands with Russia
  • 312,472
  • 42
  • 525
  • 765
2

This is an efficient way to determine the amount of free space in a circular buffer or do sliding window flow control. Use unsigned ints for head and tail - increment them and let them wrap! Buffer length has to be a power of 2.

free = ((head - tail) & size_mask), where size_mask is 2^n-1 the buffer or window size.

Flexo
  • 87,323
  • 22
  • 191
  • 272
C guy
  • 21
  • 1
  • If you're willing to take a performance hit, you can allow any positive buffer length by using the modulus operator: `free = ((head - tail) % buf_len);` – oosterwal Sep 05 '12 at 13:08
1

Just to put the already correct answer into code:

If you know that x is the smaller value, the following calculation just works:

int main()
{
    uint8_t x = 0xff;
    uint8_t y = x + 20;
    uint8_t res = y - x;
    printf("Expect 20: %d\n", res); // res is 20

    return 0;
}

If you do not know which one is smaller:

int main()
{
    uint8_t x = 0xff;
    uint8_t y = x + 20;
    int8_t res1 = (int8_t)x - y;
    int8_t res2 = (int8_t)y - x;
    printf("Expect -20 and 20: %d and %d\n", res1, res2);

    return 0;
}

Where the difference must be inside the range of uint8_t in this case.

The code experiment helped me to understand the solution better.

Tarion
  • 16,283
  • 13
  • 71
  • 107
1

The problem should be stated as follows:

Let's assume the position (angle) of two pointers a and b of a clock is given by an uint8_t. The whole circumerence is devided into the 256 values of an uint8_t. How can the smaller distance between the two pointer be calculated efficiently?

A solution is:

uint8_t smaller_distance = abs( (int8_t)( a - b ) );

I suspect there is nothing more effient as otherwise there would be something more efficient than abs().

hpc64
  • 35
  • 5
0

To echo everyone else replying, if you just subtract the two and interpret the result as unsigned you'll be fine.

Unless you have an explicit counterexample.

Your example of x = 0x2, y= 0x14 would not result in 0x4, it would result in 0xEE, unless you have more constraints on the math that are unstated.

MSN
  • 53,214
  • 7
  • 75
  • 105
0

Yet another answer, and hopefully easy to understand:

SUMMARY:

  • It's assumed the OP's x and y are assigned values from a counter, e.g., from a timer.
  • (x - y) will always give the value desired, even if the counter wraps.
  • This assumes the counter is incremented less than 2^N times between y and x, for N-bit unsigned int's.

DESCRIPTION:
A counter variable is unsigned and it can wrap around.
A uint8 counter would have values:
0, 1, 2, ..., 255, 0, 1, 2, ..., 255, ...

The number of counter tics between two points can be calculated as shown below.
This assumes the counter is incremented less than 256 times, between y and x.

uint8 x, y, counter, counterTics;
<initalize the counter>
<do stuff while the counter increments>
y = counter;
<do stuff while the counter increments>
x = counter;
counterTics = x - y;

EXPLANATION:
For uint8, and the counter-tics from y to x is less than 256 (i.e., less than 2^8):

If (x >= y) then: the counter did not wrap, counterTics == x - y

If (x < y) then: the counter wrapped, counterTics == (256-y) + x
(256-y) is the number of tics before wrapping.
x is the number of tics after wrapping.

Note: if those calculations are made in the order shown, no negative numbers are involved.

This equation holds for both cases: counterTics == (256+x-y) mod 256

For uintN, where N is the number of bits:
counterTics == ((2^N)+x-y) mod (2^N)

The last equation also describes the result in C when subtracting unsigned int's, in general.
This is not to say the compiler or processor uses that equation when subtracting unsigned int's.

RATIONALE:
The explanation is consistent with what is described in this ACM paper:
"Understanding Integer Overflow in C/C++", by Dietz, et al.

  1. HARDWARE INTEGER ARITHMETIC
    When an n-bit addition or subtraction operation on unsigned or two’s complement integers overflows, the result “wraps around,” effectively subtracting 2n from, or adding 2n to, the true mathematical result. Equivalently, the result can be considered to occupy n+1 bits; the lower n bits are placed into the result register and the highest-order bit is placed into the processor’s carry flag.

  2. INTEGER ARITHMETIC IN C AND C++
    3.3. Unsigned Overflow
    A computation involving unsigned operands can never overflow, because a result that cannot be represented by the resulting unsigned integer type is reduced modulo the number that is one greater than the largest value that can be represented by the resulting type.
    Thus, the semantics for unsigned overflow in C/C++ are precisely the same as the semantics of processor-level unsigned overflow as described in Section 2. As shown in Table I, UINT MAX+1 must evaluate to zero in a conforming C and C++ implementation.

Also, it's easy to write a C program to test that the cases shown work as described.

JimYuill
  • 76
  • 4