Подтвердить что ты не робот

Как вычесть два unsigned ints с обтеканием или переполнением

Существует два unsigned ints (x и y), которые необходимо вычесть. x всегда больше y. Однако оба x и y могут обернуться вокруг; например, если они оба являются байтами, после 0xff приходит 0x00. Дело в том, что x обертывается, а y - нет. Теперь x оказывается меньше y. К счастью, x не будет обертываться дважды (только один раз гарантирован). Предполагая байты, x обернул и теперь равен 0x2, тогда как y не имеет и равен 0xFE. Правильный ответ x-y должен быть 0x4.

Возможно,

( x > y) ? (x-y) : (x+0xff-y);

Но я думаю, что есть другой способ, что-то с участием 2-х комплиментов?, и в этой встроенной системе x и y являются самыми большими неподписанными типами int, поэтому добавление 0xff... невозможно

Каков наилучший способ написать инструкцию (целевой язык - C)?

4b9b3361

Ответ 1

Предполагая два целых числа без знака:

  • Если вы знаете, что один должен быть "больше", чем другой, просто вычтите. Он будет работать, если вы не обертываете его более одного раза (очевидно, если у вас есть, вы не сможете сказать).
  • Если вы не знаете, что один больше другого, вычтите и передайте результат в подписанный int той же ширины. Он будет работать, если разница между ними находится в диапазоне подписанного int (если нет, вы не сможете сказать).

Чтобы пояснить: сценарий, описанный в оригинальном плакате, кажется, путает людей, но типичен для монотонно увеличивающих счетчиков фиксированной ширины, таких как счетчики аппаратного таймера или порядковые номера в протоколах. Счетчик идет (например, для 8 бит) 0xfc, 0xfd, 0xfe, 0xff, 0x00, 0x01, 0x02, 0x03 и т.д., И вы знаете, что из двух значений x и y, которые у вас есть, x приходит позже. Если x == 0x02 и y == 0xfe, вычисление xy (как результат 8 бит) даст правильный ответ 4, предполагая, что вычитание двух n-битовых значений обертывает по модулю 2 n - который C99 гарантирует вычитание неподписанных значений. (Примечание: стандарт C не гарантирует такое поведение для вычитания знаковых значений.)

Ответ 2

Вот немного более подробная информация о том, почему он "просто работает", когда вы вычитаете "меньше" из "большего".

Несколько вещей, идущих в этом...
1. В аппаратном обеспечении вычитание использует добавление: соответствующий операнд просто отменяется перед добавлением.
2. В дополнении twos (которое почти все использует), целое число сбрасывается инвертированием всех битов, а затем добавлением 1.

Аппаратное обеспечение делает это более эффективно, чем это звучит из вышеприведенного описания, но это основной алгоритм вычитания (даже если значения без знака).

Итак, давайте рисуем 2 - 250, используя 8-битные целые числа без знака. В двоичном случае

  0 0 0 0 0 0 1 0  
- 1 1 1 1 1 0 1 0

Мы отрицаем вычитание операнда, а затем добавляем. Напомним, что для отрицания мы инвертируем все биты, а затем добавляем 1. После инвертирования битов второго операнда мы имеем

0 0 0 0 0 1 0 1  

Тогда после добавления 1 имеем

0 0 0 0 0 1 1 0  

Теперь мы выполняем добавление...

  0 0 0 0 0 0 1 0   
+ 0 0 0 0 0 1 1 0

= 0 0 0 0 1 0 0 0 = 8, which is the result we wanted from 2 - 250

Ответ 3

Может быть, я не понимаю, но что с этим не так:

unsigned r = x - y;

Ответ 4

Вопрос, как было сказано, запутан. Вы сказали, что вы вычитаете неподписанные значения. Если x всегда больше, чем y, как вы сказали, то x - y невозможно обернуть или переполнить. Таким образом, вы просто выполняете x - y (если это то, что вам нужно) и что оно.

Ответ 5

Это эффективный способ определения количества свободного пространства в круговом буфере или управления движением скользящего окна. Используйте unsigned int для head и tail - увеличивайте их и оберните их! Длина буфера должна быть равна 2.

free = ((head - tail) & size_mask), где size_mask равно 2 ^ n-1 размер буфера или окна.

Ответ 6

Проблема должна быть сформулирована следующим образом:

Предположим, что позиция (угол) двух указателей a и b часов задана uint8_t. Вся окружность делится на 256 значений uint8_t. Как можно эффективно вычислить меньшее расстояние между двумя указателями?

Решение:

uint8_t smaller_distance = abs( (int8_t)( a - b ) );

Я подозреваю, что нет ничего более эффективного, поскольку иначе было бы что-то более эффективное, чем abs().

Ответ 7

Чтобы откликнуться на всех остальных, если вы просто вычтете их и интерпретируете результат как unsigned, все будет в порядке.

Если у вас нет явного контрпримера.

Ваш пример x = 0x2, y= 0x14 не приведет к 0x4, это приведет к 0xEE, если только у вас больше ограничений на неустановленную математику.

Ответ 8

Просто чтобы поставить правильный ответ в код:

Если вы знаете, что x является меньшим значением, следующий расчет просто работает:

int main()
{
    uint8_t x = 0xff;
    uint8_t y = x + 20;
    uint8_t res = y - x;
    printf("Expect 20: %d\n", res); // res is 20

    return 0;
}

Если вы не знаете, какой из них меньше:

int main()
{
    uint8_t x = 0xff;
    uint8_t y = x + 20;
    int8_t res1 = (int8_t)x - y;
    int8_t res2 = (int8_t)y - x;
    printf("Expect -20 and 20: %d and %d\n", res1, res2);

    return 0;
}

В этом случае разница должна находиться внутри диапазона uint8_t.

Эксперимент с кодом помог мне лучше понять решение.