views:

228

answers:

5

Hi Folks,

I need the code to a couter that is allowed to overflow and where < > continue to tell earlier values from later values, for some defined interval.

To clarify, one possible implementation would be:

Consider two such counters cur and dut (device under test), consider two functions:

bool isEarlier(cur, dut)    // Is dut earlier than cur?
bool isLater(cur, dut)

cur and dut are 16 bit, cur has just overflowed, its current value is, lets say 5. Depending on the value of dut, the functions would return

  • 0 to 16384: isEarlier -> (cur < dut), isLater -> (cur > dut)
  • 16384 to 32768: isEarlier -> false, isLater -> true
  • 32768 to 49152: invalid, log error
  • 49152 to 65536: isEarlier -> true, isLater -> false

Update: I can write the code myself, no problem. I'm just lazy. I know for fact that there is something like that in PostgreSQL (transaction ids wrap), I just couldn't locate the function that actually does it. I am pretty sure there is something like that in the Linux kernel, probably a macro. But neighther google codesearch, nor grep over /usr/include/linux could turn it up. Any ideas where it is?

Update: Clarified role of cur and dut. The "invalid" is there as a safeguard. As the differences between cur and dut become bigger, the function eventually complains.

A: 

It seems to me you just wrote it :). Why not translate your post into some C code ?

shodanex
A: 

Bear in mind that you can't get ">" and "<" to do what you want in C. They apply to numbers only, and there is no operator overloading. The same thing applies to your exception; C does not have exceptions.

You could write some access functions that would treat unsigned integral types that way, and that wouldn't be difficult. (In C, overflow is undefined for signed integral types, although on most modern systems it wraps around.) It wouldn't be that difficult.

David Thornley
That's why I wouldn't let it overflow anyway. It might cause bad karma with analysis tools.
edgar.holleis
If analysis tools have problems with overflowing unsigned integral values, they have problems. That's perfectly legit in C.
David Thornley
+1  A: 

First compute the difference, then check into which window it falls.

Since it is so simple and the sizes of the past/future/error windows vary, you have to do it yourself.

starblue
+2  A: 

I think you're talking about handling the wraparound of the number circle correctly. It's quite easy, actually.

This doesn't do precisely what you said (not sure why you have that "exception" interval), but:

typedef unsigned short uint16_t;
typedef signed short int16_t;
// abstract out 16-bit types in case "short" doesn't correspond to 16bits

bool isEarlier(uint16_t a, uint16_t b)
{
   int16_t diff = a-b;
   return diff < 0;
}
bool isLater(uint16_t a, uint16_t b)
{
   int16_t diff = a-b;
   return diff > 0;
}

edit: this has a "branch point" at diff=-32768, so that if a=5 and b=32772, diff=-32767 which is less than 0 and hence 5 is "earlier" than 32772. If a=5 and b=32774, diff=-32769=32767 which is greater than 0 and hence 5 is "later" than 32774. This defines "earlier" and "later" in the sense of (a) the simplest math, and (b) because wraparound counters can be interpreted as having multiple solutions mod 65536, it picks the solution of a and b that are "closest" to each other with respect to the number circle.

If a and b differ by 32768 then they are equally far apart and the simple math is used to pick easiest... this "violates" the antisymmetric property of "earlier" and "later" in the sense that isLater(5,32773) is true and isLater(32773,5) is also true. But how do you know whether "5" represents a count of 5, or "5" represents a count of 65541? (just as abs(-32768) == -32768 gives an odd nonsensical answer) If you wish to maintain antisymmetry e.g. isLater(b,a) == isEarlier(a,b), then you can always do this:

bool isLater(uint16_t a, uint16_t b)
{
   int16_t diff = b-a;
   return diff < 0;
}

If you wish to bias the branch point in one direction to happen at -32768+K, then use this instead:

bool isEarlier(uint16_t a, uint16_t b)
{
   int16_t diff = a-b-K;
   return diff < -K;
}
bool isLater(uint16_t a, uint16_t b)
{
   int16_t diff = b-a-K;
   return diff < -K;
}

This no longer uses closest; if, for example, K=12768, and a=5, then for b=6,7,8,9,... 20005, isEarlier(a,b) and isLater(b,a) will be true, and for b=20006, 20007, ... 65534, 65535, 0, 1, 2, 3, 4, 5 isEarlier(a,b) and isLater(b,a) will be false.

You have a particular choice of intervals which is different from the rationale I use with wraparound numbers. The functions defined here would not meet your needs as stated, but I find those choices of interval a little peculiar. Perhaps you could explain how you determined them?

Jason S
Sorry, that doesn't work. `diff=a-b` overflows! Consider `isEarlier(10000,50000)`: It should be true, but in your code returns false. Unless of course your machine uses something else than two's complement for integer representation.
edgar.holleis
diff=a-b produces 10000-50000 smod 65536 = -40000 smod 65536 = 25536, where a smod m = (a+m/2) mod m) - m/2. 10000 is 40000 units before the next count of 50000 occurs but, only 25536 units after the last count of 50000.
Jason S
*I* consider that closer. You may not, but you have your own special definition of "earlier" and "later".
Jason S
See above edits re: branch point.
Jason S
1) Yes, your method works, the overflow is meant to be there. I didn't review your code carefully enough. I'll probably use it.
edgar.holleis
2) The initial question is not terribly clear about the meaning of the arguments. I meant the first argument to be the current counter value and the second argument the one to be checked. With a and b replaced in your code it does what I meant.
edgar.holleis
+1  A: 

Ok, for the record. Here is my solution, this is what I meant:

#include <stdint.h>


void increase_cyclic_counter (uint16_t *cnt)
{
#ifdef CYCLIC_COUNTER_EXPLICIT_WRAP
    if (*cnt < 2^16-1)
     *cnt++;
    else
     *cnt = 0;
#else
    *cnt++;
#endif
}


#define SAME 1
#define LATER 0
#define EARLIER 2
#define FORBIDDEN -1

/* dut (device under test) is tested against cur
 * returns:
 *    EARLIER (LATER) if dut happened earlier (later) in the sequence than cur
 *    SAME            if dut == cur
 *    FORBIDDEN       if dut and cur are that far away in the cyclic sequence
 *                    that no unambigious jugement is possible
 *
 * The basic idea is the same as with two-character year codes, where 
 * '97' stands for 1997 and '11' stands for 2011. '50' is regarded as 
 * too ambigous and therefore rejected.
 *
 * The implementation splits the short integer range 0-65535 into 4 parts:
 *   0-16383, 16384-32767, 32768-49151, 49152-65536
 * With cur and dut in the same range, normal arithmetics apply, else the 
 * ranges are compared to each other.
 */

int test_cyclic_counter (uint16_t cur, uint16_t dut)
{
    switch (((int)(cur>>14)) - ((int)(dut>>14)))
    {
     case 0:  // same range
      if (dut < cur) 
       return EARLIER;
      else if (dut == cur)
       return SAME;
      else 
       return LATER;

     case 1:
     case -3:
      return EARLIER;

     case 3:
     case -1:  
      return LATER;

     default: 
      return FORBIDDEN;
    }
}
edgar.holleis
test case using this code:{cur = 10000, dut = 30000} => LATER{cur = 16000, dut = 36000} => FORBIDDENdoes this make sense to you?
Jason S
I really want the feature with the forbidden range, now clarified in the original question. I am well aware that in my code the acceptable span between cur and dut are not constant. But it guarantees for every value of cur: 16384 values or more are LATER, 16384 or more EARLIER and 16384 FORBIDDEN
edgar.holleis
OK. You sound like you definitely know what you want, sorry if I was pushing at it. (it's just that this question reminded me a lot of a case at my company where we had a consultant that thought he knew what he was doing but made what should have been a very simple routine very complicated...)
Jason S