Целочисленное вычитание с переносом на N бит - PullRequest
5 голосов
/ 29 ноября 2011

В основном, поведение, которое вы получаете при переполнении целых чисел вычитанием, но для заданного количества бит. Очевидный способ, предполагая целое число со знаком :

template <int BITS>
int sub_wrap(int v, int s) {
  int max = (1<<(BITS));
  v -= s;
  if (v < -max) v += max*2;
  // or if branching is bad, something like:
  // v += (max*2) * (v < -max)
  return v;
}

// For example subtracting 24 from -16 with 5 bit wrap,
// with a range of -32, 31
sub_wrap<5>(-16, 28); -> 20

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

ОБНОВЛЕНИЕ: Извините за путаницу. Я бездумно включил вводящее в заблуждение обозначение использования количества бит, исключая бит вздоха. Таким образом, в приведенном выше тексте замените 5 битов на 6 битов для большего здравого смысла.

Ответы [ 4 ]

8 голосов
/ 29 ноября 2011

Для арифметики без знака и маскировки результатов, например:

template<int bits>
unsigned
sub_wrap( unsigned v, unsigned s )
{
    return (v - s) & ((1 << bits) - 1);
}

В более общем случае вы можете использовать оператор по модулю:

template<int modulo>
unsigned
sub_wrap( unsigned v, unsigned s )
{
    return (v - s) % modulo;
}

(Обтекание по n битамэквивалент модуля 2 ^ n.)

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

РЕДАКТИРОВАТЬ: Используя предложение sehe для арифметики со знаком:

template<int bits>
int
sub_wrap( int v, int s )
{
    struct Bits { signed int r: bits; } tmp;
    tmp.r = v - s;
    return tmp.r;
}

Учитывая это, sub_wrap<5>( -16, 28 ) дает-12 (что правильно - обратите внимание, что 28 не может быть представлен как целое число со знаком в 5 битах);sub_wrap<6>( -16, 28 ) дает 20.

5 голосов
/ 29 ноября 2011

Полагаю, это должно работать:

 struct bits
 {
     signed int field : 5;
 };

 bits a = { -16 };     
 bits b = {  28 };

 bits c = { a.field - b.field };
 std::cout << c.field << std::endl;

Я почти уверен, что ширина поля не будет работать с аргументом шаблона const ... и, следовательно, это менее универсально.Следует, однако, избегать ручного поворота.Скоро опубликует тест

Обновление Оказывается, мой ответ все-таки был неверным.Просто образец ввода (28) не может быть представлен в 5 битах (со знаком).Результат вышеупомянутого -12 ( см. http://ideone.com/AUrXy).

Вот, для полноты, шаблонная версия в конце концов:

template<int bits>
int sub_wrap(int v, int s)
{
    struct helper { signed int f: bits; } tmp = { v };
    return (tmp.f -= s);
}
2 голосов
/ 29 ноября 2011

Вот как я могу это сделать без условных ветвлений и умножения:

#include <stdio.h>

// Assumptions:
// - ints are 32-bit
// - signed ints are 2's complement
// - right shifts of signed ints are sign-preserving arithmetic shifts
// - signed overflows are harmless even though strictly speaking they
//   result in undefined behavior
//
// Requirements:
// - 0 < bits <= 32
int sub_wrap(int v, int s, unsigned bits)
{
  int d = v - s;
  unsigned m = ~0u >> (32 - bits);
  int r = d & m | -((d >> (bits - 1)) & 1) & ~m;
  return r;
}

#define BITS 2

int main(void)
{
  int i, j;
  for (i = -(1 << (BITS - 1)); i <= (1 << (BITS - 1)) - 1; i++)
    for (j = -(1 << (BITS - 1)); j <= (1 << (BITS - 1)) - 1; j++)
      printf("%d - %d = %d\n", i, j, sub_wrap(i, j, BITS));
  return 0;
}

Вывод:

-2 - -2 = 0
-2 - -1 = -1
-2 - 0 = -2
-2 - 1 = 1
-1 - -2 = 1
-1 - -1 = 0
-1 - 0 = -1
-1 - 1 = -2
0 - -2 = -2
0 - -1 = 1
0 - 0 = 0
0 - 1 = -1
1 - -2 = -1
1 - -1 = -2
1 - 0 = 1
1 - 1 = 0
1 голос
/ 29 ноября 2011

Имитирует целочисленную n-битную операцию:

#include <iostream>
#include <cstdlib>

template< typename T >
T sub_wrap(T a, T b, int nBits)
{
        T topBit, mask, tmp;

        topBit=T(1) << (nBits-1);
        mask=(topBit << 1)-1;
        tmp=((a&mask)+((~b+1)&mask))&mask;
        if (tmp & topBit) tmp=-((~tmp&mask)+1);

        return tmp;
}

int main(int argc, char* argv[])
{
        std::cout << sub_wrap< int >(atoi(argv[1]), atoi(argv[2]), atoi(argv[3]))
                << std::endl;
        return 0;
}

Результаты:

$ ./sim 5 6 4
-1
$ ./sim 7 3 4
4
$ ./sim 7 -1 4
-8
$ ./sim -16 28 4
4
$ ./sim -16 28 5
-12
$ ./sim -16 28 6
20

Кажется, вы неправильно рассчитали размер шрифта на 1 бит.

...