3

I am trying to check if a received message number is in a given range. Everytime the message number gets incremented.So if I am expecting number 10, I accept any message with number 10+ 5. So sequence numbers 10 to 15. I am using an unsigned int. So when expected number is 65532,I can accept 65532 + 10(so min = 65532 and max = 5). How do I check if the number I received is in this range?

dm25
  • 33
  • 1
  • 3
  • well,I am using a 32 bit unsigned integer.I know its a huge number but want to handle the condition anyway. – dm25 Jul 11 '13 at 04:27
  • 1
    Your first example has a range of 6 (10, 11, 12, 13, 14, 15) and then your second has 10 (65532, 65533, 65534, 65535, 0, 1, 2, 3, 4, 5), and assumes 16-bit numbers, but you say you're using 32-bit numbers. Still, the answer is clear enough. :-) – torek Jul 11 '13 at 04:47
  • I think I was not clear with my question. Lets say the minValue is 65532. I can accept msg numbers 65532,65533,65534,65535,65536,0,1,2,3,4,5,6. Now I get a Message with number 65534 (valid) or a message with number 4 (also valid). How do I check if these message numbers are valid? – dm25 Jul 11 '13 at 14:59
  • @dm25 - see my answer below – Mark Lakata Jul 11 '13 at 17:06
  • @dm25: "I can accept msg numbers 65532,65533,65534,65535,65536,0,1,2,3,4,5,6" -- I don't think that's right, since 65536 is not representable in a 16-bit unsigned type. I think you meant 65532,65533,65534,65535,0,1,2,... And it's unclear how big a range you'll accept; in one case you say 10 to 15 (6 numbers), in another you seem to be saying you'll accept any of 11 different numbers. The exact width of the range is probably not important, but consistency is. – Keith Thompson Jul 11 '13 at 17:23
  • Please provide detail. @Mark Lakata concerns are valid depending on what your unspecified details. 1. What type is your "message number"? 2. What is the size or range of your machines `unsigned int` type? 3. Confirm the range of values in "message number" is 0 ... 65,535 or whatever. – chux - Reinstate Monica Jul 11 '13 at 22:18

3 Answers3

6

Simply subtract

unsigned message_number, expected_number;
unsigned range = 5;  // or 10,  OP’s post varies as to the desired range.
if ((message_number - expected_number) <= range) {
  ;  // accept;
}

Wrap-around of unsigned arithmetic is well defined.

[Edit]

The above solution works well if the message_number wraps around at the same place as unsigned. The following solutions do not make that assumption, neither do they assume message_number wraps around at unsigned short.

unsigned maxsequenceplus1_number = 65536LU;
if (((message_number - expected_number)%maxsequenceplus1_number) <= range) {
  ;  // accept;
}

const unsigned maxsequence_number = 65535U; // some power of 2 minus 1
if (((message_number - expected_number)&maxsequence_number) <= range) {
  ;  // accept;
}
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
  • 3
    In a sense, you are doing unsigned arithmetic judo: You are using its own energy against it. – Raymond Chen Jul 11 '13 at 04:42
  • @Raymond Chen "arithmetic judo" - need to remember that phrase. – chux - Reinstate Monica Jul 11 '13 at 04:48
  • Please see my answer. This answer will work as long as `unsigned` (or more explicitly `unsigned int`) is the same as the machine register size. It will break if it is not. – Mark Lakata Jul 11 '13 at 05:43
  • 1
    @MarkLakata The C standard requires that unsigned arithmetic behave modulo 2^n for some n. In the OP's case, n=16 since they are worried about the 65536->0 overflow. If their n were 32, they would be worried about the 4294967295->0 overflow. The above solution works for unsigned overflow regardless of n. Of course, if you want to force premature wraparound at 65536, then you need to handle that explicitly, but from context, it appears that the question was about unsigned wraparound, not 16-bit wraparound. – Raymond Chen Jul 11 '13 at 05:53
  • @Mark Lakata Your answer's concern about using `unsigned short` with `unsigned` and `int` is valid, but does not apply here. The arithmetic has no type nor size conversions and uses `unsigned` which is of the machine's working integer size. – chux - Reinstate Monica Jul 11 '13 at 14:19
  • I think I was not clear with my question. Lets say the minValue is 65532. I can accept msg numbers 65532,65533,65534,65535,65536,0,1,2,3,4,5,6. Now I get a Message with number 65534 (valid) or a message with number 4 (also valid). How do I check if these message numbers are valid? – dm25 Jul 11 '13 at 15:11
  • 1
    @dm25 I'm pretty sure this answer is correct. Try it with 4-bit numbers (0 to 15) just so it's easy (and you can do it on paper). Make MinValue (expected_number) 12. Say your range is 10. Say your accept number is 3. 3 - 10 in unsigned arithmetic is 9. 9 is less-than or equal-to 10 (the range) Therefore "3" is in range. Try it with some other numbers. – Pete Baughman Jul 11 '13 at 16:41
  • @PeteBaughman - actually this will not work. Try it with the values given. `(unsigned)4 - (unsigned)65532 == (unsigned) 0xFFFF0008`. 0xFFFF0008 is not less than 10. If you cast the values to (unsigned short), then it works, since `(unsigned short)4 - (unsigned short)65532 == (unsigned short)8`. Please see my answer below for the correct method. – Mark Lakata Jul 11 '13 at 16:50
  • @Mark Lakata (unsigned)4 - (unsigned)65532 is 0xFFFF0008 on a 32-bit machine, **8** on OP's 16-bit machine and 0xFFFFFFFFFFFF0008 on a 64-bit machine. The 16-bit machine is implied by the OP's ",I can accept 65532 + 10(so min = 65532 and max = 5)" and elsewhere. This works for the OP. It also works for your 32-bit machine when the values are near 4,294,967,296. – chux - Reinstate Monica Jul 11 '13 at 21:41
  • @dm25 I think you mean "...65534,65535,0,1,2,3...", not "...65534,65535,65536,0,1,2,3...". Your `unsigned int` range from 0 ... 65,535. – chux - Reinstate Monica Jul 11 '13 at 22:00
  • @Mark Lakata The OP has sequence wrap around at 64K, this implies a 16 machine, or at least a 16 bit counter for the sequence number. OP also states "well,I am using a 32 bit unsigned integer." which supports your concern, but I do not know if his `32 bit unsigned integer` is a `unsigned long` or `unsigned`. – chux - Reinstate Monica Jul 11 '13 at 22:42
  • I would not assume the OP is using a 16-bit machine. 16-bit values exist all the time in protocols, for example, communication protocols use a sequence field that might be only 16 bits. – Mark Lakata Jul 12 '13 at 00:11
  • @Mark Lakata good point about 16-bit values communication protocols. – chux - Reinstate Monica Jul 12 '13 at 02:15
1

If you use unsigned you get GF(2k) arithmetic for some value k [Edit: addition and subtraction, doing full finite field is much more work; maybe I should adopt some other shorthand terminology?]. Typically for unsigned short k is 16, for unsigned int it is 32, and for unsigned long long it is 64, but in any case it is at least 16 (because UINT_MAX is at least 65535).

In this kind of finite field arithmetic you can simply subtract two numbers and compare the result against your limit. That is, if the range of "allowed values" is from x to x+5 and the actual value you received is y, then:

unsigned int x, y, diff;
...
diff = y - x;
if (diff <= 5) {
    the value is in range
} else {
    the value is out of range
}

This works fine as long as the "in range" window does not exceed 2k-1, and since k >= 16 that means your window space is at least 32767.

If you wish to use unsigned short the one wrinkle in C is that unsigned short expands to (signed, plain) int instead of unsigned int in the usual case, when INT_MAX >= USHRT_MAX. So you have to cast to unsigned int to do the subtraction:

unsigned short x, y, diff;
....
diff = (unsigned int)y - (unsigned int)x;

The rest of the code is unchanged (note that assigning unsigned int back to unsigned short is well defined as it reduces the value mod 2k).

torek
  • 448,244
  • 59
  • 642
  • 775
  • If you use unsigned short, you do *not* need to cast x and y to (unsigned int). The code will work fine without the casts. This works because you are storing the value back into diff which is `unsigned short`, the same as x and y – Mark Lakata Jul 11 '13 at 05:37
  • It's not *guaranteed* to work without the cast. For instance, if y is 2 and x is 65535 and INT_MAX is 131071 but INT_MIN is -32767, the difference will be -65533 which will result in overflow. This implementation is sort of absurd (I've never seen anything like this), but the cast gives you a solid guarantee. – torek Jul 11 '13 at 05:50
  • I'm fairly sure that C requires that integers use twos-complement arithmetic. That guarantees INT_MAX = -(INT_MIN+1) in all cases (for signed integers). You don't need the cast. – Mark Lakata Jul 11 '13 at 17:01
  • btw, INT_MIN is -32768 not -32767 if INT is 16 bits. INT_MAX is 32767. They are not symmetric. – Mark Lakata Jul 11 '13 at 17:02
  • You're assuming 2's complement. The weirder machines are typically 1s' complement. Ones' complement implementations do (or did) exist, e.g., on the Univac 11xx. – torek Jul 11 '13 at 21:21
  • 1
    you are right, C allows 1s complement machines, but I've never ever come across one. http://stackoverflow.com/questions/12276957/are-there-any-non-twos-complement-implementations-of-c – Mark Lakata Jul 12 '13 at 00:17
  • Yes. The casts are *mostly* just defending against unlikely insanity, but the standards people made the wrong (my opinion) decision on the unsigned widening rules way back when. The Johnson compiler used the "narrow unsigned (short or char) widens to wider unsigned (int)" rule, which, since finite field arithmetic is well behaved, is in my opinion a *much* better way to do it than the "maybe signed, maybe unsigned" rule they adopted to "prevent surprises". Except of course now you have to #ifdef USHRT_MAX < INT_MAX to "prevent surprises", surprise! :-) – torek Jul 12 '13 at 00:42
  • Um, unsigned integers are not a Galois field. It's not even a field! In GF(2^k), addition is XOR, which is definitely not how unsigned integers behave (unless k=1). Multiplication is extremely complicated in a Galois field. – Raymond Chen Jul 19 '13 at 13:43
  • @RaymondChen: Er, yes, I overstated, it's only for addition and subtraction. (You can *implement* GF(2^k) with unsigned arithmetic. Well, strictly speaking you could do it with signed, it's just way easier with unsigned.) – torek Jul 19 '13 at 22:46
  • @torek: What's really needed is a set of algebraic ring types which are distinct from "number" types. Numbers would implicitly convert to rings, but rings would not implicitly convert to numbers nor to other size rings. One would have to keep the horrible behaviors of existing types, but the use of the "number" and "ring" types could be encouraged in new code. – supercat Feb 24 '14 at 19:31
1

To get the right result with 16 bit integers (ie range 0 to 65535), you can use unsigned short to store the values. Then the roll over will work just fine.

BUT you need to be careful. Just because you have all variables declared as unsigned short, the difference between 2 unsigned shorts can actually be automatically cast to int, which will will break your code.

For example,

void test(unsigned short incoming, unsigned short min, unsigned short range)
{
 if ((incoming - min) >= 0 && (incoming - min) <  range)
   printf("in range");       
 else
   printf("out of range");
}

This fails if you enter incoming=5 and min=0xFFFF and range=5. It will print "in range" even though it is not in range.

To fix this problem, you need to do

void test(unsigned short incoming, unsigned short min, unsigned short range)
{
 if ((unsigned short)(incoming - min) >= 0 &&
     (unsigned short)(incoming - min) <  range)
   printf("in range");       
 else
   printf("out of range");
}

This is a contentious point in the c language. The specification allows subtraction to be carried out in registers that are larger than the given data size (short). Most computers have 32 bit or 64 bit registers, so the subtraction is carried out by int registers which are signed. So 0x00000005 - 0x0000FFFF = 0xFFFF0006, which is negative (-65530) and not just 6. You need to recast back to the original data type (unsigned short) to get back the 6.

Please see In a C expression where unsigned int and signed int are present, which type will be promoted to what type? for a discussion on integer promotion.

You can also do this, which is probably the best answer. I took out the comparison for delta >= 0 since that is always true for unsigned integers. And also switched to using stdint.h

#include <stdint.h>

void test(uint16_t incoming, uint16_t  min, uint16_t  range)
{
 uint16_t  delta = incoming - min;
 if (delta <  range)
   printf("in range");       
 else
   printf("out of range");
}
Community
  • 1
  • 1
Mark Lakata
  • 19,989
  • 5
  • 106
  • 123
  • This approach seems to depend on `unsigned short` being 16 bits. Does C defines `unsigned short` to be 16 bits? – chux - Reinstate Monica Jul 12 '13 at 02:19
  • No, I was being sloppy. Most 32 bit systems define `short` as 16 bits, but that is not guaranteed. In this day and age, any compiler that doesn't define `short` as 16 bits is asking for trouble, but it is technically allowed. It is better to use the standard integer typedefs in `` which means `uint16_t` instead of `unsigned short`. (As a side note, I did use a PIC compiler that defined `short` to mean ONE bit. That caused a lot of confusion and broken code!) – Mark Lakata Jul 12 '13 at 16:10