8

Apologies for my imbecility as this is my first post on this forum. I am trying to detect the difference between a wrapping unsigned 32-bit counter and a large negative Jump with the help of following code but the compiler give me error:

error: comparison is always true due to limited range of data type [-Werror=type-limits]

Here is my code snippet:

#define MAX_BACKWARD_JUMP -4294959295 //UINT_MAX - 8000
#define MIN_BACKWARD_JUMP -3600
#define MAX_FORWARD_JUMP   4800000

signed int rtpDelta; //Signed 32-bit
unsigned int currRTPTs, prevRTPTs; //unsigned 32-bit

rtpDelta = currRTPTs - prevRTPTs;

  if ((rtpDelta > MAX_BACKWARD_JUMP && rtpDelta < MIN_BACKWARD_JUMP) 
        || (rtpDelta > MAX_FORWARD_JUMP))
        {
          printf("Delta in Timestamps too large\n",rtpDelta);
        }

The idea here is to catch the invalid large Deltas in RTP timestamps. We have a current TimeStamp and a previous Timestamp receiving from peer RTP client. The boundary limits for invalid values of RTP Timestamps are -4294959295 < rtpDelta < -3600 that is it should throw an Error if the Delta is less than -3600 and greater than -4294959295 because the values closer to UMAX_INT will be considered as roll-over. What am I doing wrong here?

Black_Zero
  • 445
  • 1
  • 6
  • 12
  • 1
    For a start, `-4294959295` is not representable with a signed 32-bit integer. – Oliver Charlesworth Feb 16 '14 at 11:06
  • Very true, but I can't go beyond 32-bit and I need signed integer to keep the result of deltas in negative. How can I acheive that? – Black_Zero Feb 16 '14 at 11:17
  • 1
    Minimum value for 32-bit int is `1-2^31`, which is `-2147483647`. – Synxis Feb 16 '14 at 11:20
  • I understand but If I use unsigned 32-bit I will loose the negativity. – Black_Zero Feb 16 '14 at 11:25
  • Most probably you loose negativity anyway. In my debugger i see that MAX_BACKWARD_JUMP converted to unsigned int with value 8001 – Dabo Feb 16 '14 at 11:31
  • 1
    @Synxis: off-topic, but the minimum int value actually is `-2^31`, which is `-2147483648`. – Martin J. Feb 16 '14 at 11:36
  • 2
    @MartinJ: The minimum value you may rely on in portable code for signed 32-bit integer is 1-2**31, since C permits signed integers to use two’s complement, one’s complement, or sign-magnitude. – Eric Postpischil Feb 16 '14 at 11:57
  • @EricPostpischil: And the minimum value of your current implementations can be found by including `` and gently asking `std::numeric_limits::min()` – Matthieu M. Feb 16 '14 at 12:58
  • I don't get the purpose of the range `(MAX_BACKWARD_JUMP, MIN_BACKWARD_JUMP)` –  Feb 16 '14 at 13:03
  • @EricPostpischil: Ah, I was not aware that binary representations other than 2's complement were supported by the C++ standard... live and learn. – Martin J. Feb 16 '14 at 13:59
  • @Dieter Lücking: its perfectly normal for RTP TimeStamps to be negative but not too much thats why MIN_BACKWARD_JUMP and incase of a wrap around the delta will again result in a huge negative value which is perfectly normal thats why MAX_BACKWARD_JUMP – Black_Zero Feb 16 '14 at 15:04

4 Answers4

5

In general, if you have two unsigned counters a and b, with values between 0 and LIMIT-1, inclusive, with the data type capable of representing 2*LIMIT-1, you can use modulo arithmetic with the split point in the middle:

difference = (a + LIMIT - b) % LIMIT;
if (difference <= LIMIT/2) {
    /* a = b + difference */
} else {
    /* b = a + (LIMIT - difference) */
}

It is common to have LIMIT be a power of two, in which case the modulo operator (% LIMIT) can be replaced with a binary AND (& (LIMIT-1)), which is much faster on current processors.

For C, unsigned integer types are defined in the standards as having modulo arithmetic (C99, C11 6.2.5p9), so a - b using any unsigned integer type for a and b will yield the correct results, with LIMIT being the corresponding Utype_MAX macro defined in "limits.h" header file. For example,

const unsigned int  d = (unsigned int)a - (unsigned int)b;
if (d <= UINT_MAX/2)
    /* a >= b, a = b + d */
else
    /* a < b,  b = a + UINT_MAX - (d - 1) */
Nominal Animal
  • 38,216
  • 5
  • 59
  • 86
  • 1
    Also, to detect if `a+b` produced a carry (unsigned wraparound), just check `a > (a+b)`. (You don't need to also check `b > (a+b)`.) See [this question on addition with unsigned saturation](http://stackoverflow.com/a/166393/224132) for simple C code that produces good asm when checking for wraparound. – Peter Cordes Mar 18 '16 at 06:05
  • 2
    @PeterCordes: Good point! I hadn't realized that before -- but now that you pointed it out, it's kind of obvious! (I posted this "answer" only because I [answered](http://stackoverflow.com/a/36000952/5977581) a related question by the asker, applying these for circular buffer implementation.) – Nominal Animal Mar 18 '16 at 06:14
1

Consider:

unsigned int LowerBound = -3600u, UpperBound = 4800000u;

unsigned int difference = currRTPTs - prevRTPTs;

Observe that, due to wrapping, the value of LowerBound, -3600u, will be a large positive integer. Now, when the mathematical difference (calculated without overflow) is less than -3600 by a reasonable amount, the value of difference will be a large integer, and it will be less than LowerBound. Additionally, if the difference does not become too great (in the negative direction), then difference will remain greater than UpperBound.

Similarly, if the difference is greater than 4,800,000 by a reasonable amount, the value of difference will be greater than UpperBound. If the difference does not become too much greater, then it will remain less than LowerBound.

Thus, in both cases, the value of difference when the mathematical difference is outside the desired bounds (but not by too much) is less than LowerBound and greater than UpperBound:

if (difference < LowerBound && difference > UpperBound)
    printf("Delta in timestamps is outside acceptable bounds.\n");

Observe that this will fail when the mathematical difference exceeds -3600u (which is 4,294,967,296 - 3600) or is less than 4,800,000 - 4,294,967,296. Thus, the test works when the difference is in [-4,290,167,296, 4,294,963,696].

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
  • Thank you very much for the Answer but it does'nt address the issue of negative Delta [MAX_BACKWARD_JUMP, MIN_BACKWARD_JUMP]. Let's forget the MAX_FORWARD_JUMP boundary here because its trivial. The challenge is to detect the difference when the 32-bit counter is rolled-over OR when it has a good value which is less than `-(UINT_MAX-8000)` with the help of a `Signed int`. – Black_Zero Feb 16 '14 at 15:43
  • @Black_Zero: Look again. The test in this answer delivers the desired result for all cases in which the change from `prevRTPTs` to `currRTPTs` is in the interval [-4,290,167,296, 4,294,963,696]. It is of no consequence whether wrapping occurred between the two values; this test works regardless. Further, this is the best possible result because, if the change is outside that interval, the values of `currRTPTs` and `prevRTPTs` are identical to values that could occur with a change inside the desired interval of [-3600, 4,800,000], and therefore no distinction would be possible. – Eric Postpischil Feb 16 '14 at 15:56
  • @Black_Zero: If you do not believe this test delivers the desired result, then show specific values for `prevRTPTs` and `currRTPTs` for which the result is incorrect. – Eric Postpischil Feb 16 '14 at 15:56
  • `currRTPTs = 0, prevRTPTs = 4294958296`. The `difference` is an invalid value and code should trigger but it does not. – Black_Zero Feb 16 '14 at 16:52
  • 2
    @Black_Zero: Changing from 4,294,958,296 to 0 is a forward jump of 9000 units. This is inside the acceptable changes of [-3600, 4,800,000] and should not produce an error. The test is correct. If you view as a change from 4,294,958,296 to 0 as a jump of -4,294,958,296 units then, as I wrote previously, it is **impossible** to distinguish this case from the acceptable forward jump. If you require the software to distinguish forward jumps of 9000 from backward jumps of 4,294,958,296 using only the contents of `currRTPTs` and `prevRTPTs`, then the problem is insolvable. – Eric Postpischil Feb 16 '14 at 17:02
0

It appears to me things are made complicated, unnecessarily:

#include <cstdio>
#include <cstdlib>
#include <limits>

#define COMPLICATED 0

#if COMPLICATED

int main()
{
    const unsigned MAX_BACKWARD_JUMP = std::numeric_limits<unsigned>::max() - 8000;
    const unsigned MIN_BACKWARD_JUMP = 3600;
    const unsigned MAX_FORWARD_JUMP = 4800000;
    unsigned prevRTPTs = 0;
    for(unsigned i = 0; i < 10; ++i) {
        unsigned currRTPTs = std::rand();
        std::printf("previous = %10d: ", prevRTPTs);
        std::printf(" current = %10d: ", currRTPTs);
        if(currRTPTs < prevRTPTs) {
            // Negative
            unsigned rtpDelta = prevRTPTs - currRTPTs;
            // Why a range and no threshold?
            if(MIN_BACKWARD_JUMP < rtpDelta && rtpDelta < MAX_BACKWARD_JUMP)    {
                std::printf("Invalid range: %10d\n", rtpDelta);
            }
            else {
                std::printf("           OK: %10d\n",rtpDelta);
            }

        }
        else {
            // Positive
            unsigned rtpDelta = currRTPTs - prevRTPTs;
            if(MAX_FORWARD_JUMP < rtpDelta) {
                std::printf("    Too large: %10d\n",rtpDelta);
            }
            else {
                std::printf("           OK: %10d\n",rtpDelta);
            }
        }
        prevRTPTs = currRTPTs;
    }
}

#else

int main()
{
    const unsigned MAX_JUMP = 4800000;
    unsigned prevRTPTs = 0;
    for(unsigned i = 0; i < 10; ++i) {
        unsigned currRTPTs = std::rand();
        std::printf("previous = %10d: ", prevRTPTs);
        std::printf(" current = %10d: ", currRTPTs);
        unsigned rtpDelta = currRTPTs - prevRTPTs;
        if(currRTPTs < rtpDelta) {
            // Negative (Underflow)
            rtpDelta = prevRTPTs - currRTPTs;

        }
        if(MAX_JUMP < rtpDelta) {
            std::printf("    Too large: %10d\n",rtpDelta);
        }
        else {
            std::printf("           OK: %10d\n",rtpDelta);
        }
        prevRTPTs = currRTPTs;
    }
}

Note: all RTPTs values are unsigned.

0

There is only one way to reliably check for overflow: Check before calculating the sum. Like this:

unsigned old = ..., delta = ...;
if((int)delta > 0) {    //cast to int, because we need to interprete negative numbers here.
    if(old > maxvalue - delta) printf("Overflow!\n");    //No signed arithmetic, so we don't trigger undefined behavior.
} else {
    if(old < minvalue - delta) printf("Underflow!\n");
}

It is important that the delta appears on the right side of the comparisons.

Now, what to do, if you don't have access to the delta before it gets added to the sum? Luckily, you can simply recover the delta:

unsigned old = ..., new = ...;
unsigned delta = new - old;
//Now you can proceed as above.

This works, because unsigned subtraction is truly the inverse of unsigned addition, there are no uninvertible cases or undefined behavior that could be triggered.

cmaster - reinstate monica
  • 38,891
  • 9
  • 62
  • 106