Является ли обнаружение неподписанного обертывания посредством приведения в подписанное поведение undefined?

Я использую uint16_t как счетчик последовательности в сетевом протоколе. Этот счетчик обычно обертывается, как ожидалось. Когда получатель получает пакет, он проверяет этот счетчик на самый последний полученный, чтобы увидеть, является ли он новым пакетом или пакетом вне очереди.

При сравнении порядковых номеров необходимо учитывать значение обхода. Итак, если, например, последний порядковый номер был 0x4000, то порядковый номер от 0x4001 до 0xBFFF является более новым, а порядковый номер от 0xC000 до 0xFFFF и от 0x0000 до 0x3FFF равен меньше.

В настоящее время я делаю следующее:

uint16_t last;
uint16_t current;
...
// read in values for last and current
...
if ((int16_t)(current - last) > 0) {
    printf("current is newer\n");
} else {
    printf("current is older (or same)\n");
}

Вычитая два и обрабатывая результат как a int16_t, я легко могу видеть, что больше и на сколько. Например, если текущий порядковый номер не менее 5 меньше последнего, то i.e ((int16_t)(current - last) < -5), я могу предположить, что это не связано с обычным переупорядочением пакетов и удалением пакета.

Я понимаю, что подписанное wraparound - это undefined, но в этом случае я обрабатываю значение unsigned, подписанное для сравнения. Это вызывает поведение undefined, и если да, то каким будет лучший способ сделать этот тип сравнения?

Ответ 1

Поведение преобразования вне диапазона определяется реализацией.

Почему бы вам просто не полностью устранить эту проблему и написать:

if ( current != last && current - last < 0x8000 )
    printf("current is newer\n");
else
    printf("current is older (or same)\n");

Примечание. Этот ответ относится только к конкретному вопросу с участием uint16_t. Для других типов потребуется другой код.

Ответ 2

Вы можете отбрасывать uint16_t до int32_t и выполнять вычитание.

if (((int32_t) current - (int32_t) last) > 0) {
    printf("current is newer\n");
} else {
    printf("current is older (or same)\n");
}

Примечание: при нажатии на int16_t, uint16_t больше 32767 будет отображаться как отрицательное (в значении целого числа старший бит, установленный в 1, означает отрицательный, в неподписанных типах это просто обычный бит).

Ответ 3

Вычитание будет происходить с использованием продвинутого типа, если int не равно 16 бит, и в этом случае поведение определяется реализацией.

Чтобы быть в безопасности, вы можете вычислить абсолютную разницу с помощью тройного:

current > last ? current - last : last - current