如何在C语言中检测包装计数器和大负值之间的差异

Bla*_*ero 5 c c++ x86 32-bit rtp

为我的愚蠢道歉,因为这是我在这个论坛上的第一篇文章.我试图通过以下代码检测包装无符号32位计数器和大负跳转之间的区别,但编译器给我错误:

错误:由于数据类型的范围有限,比较始终为真[-Werror = type-limits]

这是我的代码片段:

#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);
        }
Run Code Online (Sandbox Code Playgroud)

这里的想法是在RTP时间戳中捕获无效的大型Deltas.我们有一个当前的TimeStamp和一个从对等RTP客户端接收的前一个Timestamp.RTP时间戳的无效值的边界限制是-4294959295 <rtpDelta <-3600,如果Delta小于-3600且大于-4294959295,它应该抛出错误,因为更接近UMAX_INT的值将被视为翻转.我在这做错了什么?

Nom*_*mal 5

一般来说,如果您有两个无符号计数器ab,其值在 0 和LIMIT-1之间(含 0 和 ),且数据类型能够表示2*LIMIT-1,则可以使用中间分割点的模算术:

difference = (a + LIMIT - b) % LIMIT;
if (difference <= LIMIT/2) {
    /* a = b + difference */
} else {
    /* b = a + (LIMIT - difference) */
}
Run Code Online (Sandbox Code Playgroud)

通常是LIMIT2 的幂,在这种情况下,模运算符 ( % LIMIT) 可以替换为二进制 AND ( & (LIMIT-1)),这在当前处理器上要快得多。

对于 C,无符号整数类型在标准中定义为具有模运算(C99、C11 6.2.5p9),因此a - b使用任何无符号整数类型 forab将产生正确的结果,并且是头文件中定义LIMIT的相应宏。例如,Utype_MAX"limits.h"

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) */
Run Code Online (Sandbox Code Playgroud)

  • @PeterCordes:好点!我以前没有意识到这一点 - 但现在你指出了这一点,这是显而易见的!(我发布这个“答案”只是因为我[回答](http://stackoverflow.com/a/36000952/5977581)提问者的一个相关问题,将它们应用于循环缓冲区实现。) (2认同)

Eri*_*hil 1

考虑:

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

unsigned int difference = currRTPTs - prevRTPTs;
Run Code Online (Sandbox Code Playgroud)

观察到,由于环绕, , , 的值LowerBound-3600u是一个很大的正整数。现在,当数学差值(在不溢出的情况下计算)小于 -3600 一个合理的量时, 的值difference将是一个大整数,并且它将小于LowerBound。此外,如果差异没有变得太大(在负方向上),则将difference保持大于UpperBound

同样,如果差异大于 4,800,000 且合理,则 的值difference将大于UpperBound。如果差异没有变得太大,那么它将保持小于LowerBound

difference因此,在这两种情况下,当数学差异超出所需范围(但不是太多)时的值小于LowerBound和大于UpperBound

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

请注意,当数学差异超过-3600u(即 4,294,967,296 - 3600)或小于 4,800,000 - 4,294,967,296 时,此操作将会失败。因此,当差异在 [-4,290,167,296, 4,294,963,696] 范围内时,测试有效。

  • @Black_Zero:从 4,294,958,296 更改为 0 是向前跳跃 9000 个单位。这是在 [-3600, 4,800,000] 可接受的变化范围内,不应产生错误。测试是正确的。如果您将从 4,294,958,296 到 0 的变化视为 -4,294,958,296 个单位的跳跃,那么,正如我之前所写,**不可能**将这种情况与可接受的向前跳跃区分开来。如果你要求软件仅使用“currRTPTs”和“prevRTPTs”的内容来区分向前跳转9000和向后跳转4,294,958,296,那么问题是无法解决的。 (2认同)