Извиняюсь за мою слабость, так как это мой первый пост на этом форуме. Я пытаюсь обнаружить разницу между упаковочным 32-разрядным счетчиком без знака и большим отрицательным переходом с помощью следующего кода, но компилятор выдает мне ошибку:
ошибка: сравнение всегда верно из-за ограниченного диапазона типа данных [-Werror = type-limit]
Вот мой фрагмент кода:
#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);
}
Идея здесь состоит в том, чтобы перехватить недопустимые большие дельты в метках времени RTP. У нас есть текущая метка времени и предыдущая метка времени, полученная от однорангового RTP-клиента. Граничные пределы для недопустимых значений меток времени RTP -4294959295 < rtpDelta < -3600, то есть он должен выдавать ошибку, если дельта меньше -3600 и больше -4294959295, потому что значения, близкие к UMAX_INT, будут рассматриваться как опрокидывание. Что я здесь не так делаю?
Рассматривать:
unsigned int LowerBound = -3600u, UpperBound = 4800000u;
unsigned int difference = currRTPTs - prevRTPTs;
Обратите внимание, что из-за 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");
Обратите внимание, что это не удастся, когда математическая разница превышает -3600u
(что составляет 4 294 967 296 — 3600) или меньше 4 800 000 — 4 294 967 296. Таким образом, тест работает, когда разница составляет [-4,290,167,296, 4,294,963,696].
В общем, если у вас есть два неподписанных счетчика a
а также b
со значениями от 0 до LIMIT-1
включительно, с типом данных, способным представлять 2*LIMIT-1
, вы можете использовать арифметику по модулю с точкой разделения в середине:
difference = (a + LIMIT - b) % LIMIT;
if (difference <= LIMIT/2) {
/* a = b + difference */
} else {
/* b = a + (LIMIT - difference) */
}
Это обычно иметь LIMIT
быть степенью двойки, в этом случае оператор по модулю (% LIMIT
) можно заменить двоичным И (& (LIMIT-1)
), что намного быстрее на современных процессорах.
Для C целочисленные типы без знака определены в стандартах как имеющие арифметику по модулю (C99, C11 6.2.5p9), поэтому a - b
используя любой беззнаковый целочисленный тип для a
а также b
даст правильные результаты, с 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) */
Мне кажется, что все усложняется без необходимости:
#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;
}
}
Примечание: все значения RTPT не имеют знака.
Существует только один способ надежной проверки на переполнение: проверьте перед вычислением суммы. Как это:
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");
}
Важно, чтобы дельта отображалась с правой стороны сравнений.
Что делать, если у вас нет доступа к дельте до ее добавления к сумме? К счастью, вы можете просто восстановить дельту:
unsigned old = ..., new = ...;
unsigned delta = new - old;
//Now you can proceed as above.
Это работает, потому что беззнаковое вычитание действительно является обратным к беззнаковому сложению, нет никаких необратимых случаев или неопределенного поведения, которое может быть вызвано.