Какой самый эффективный способ в Java подписать расширение произвольной длины битов?

Скажем, например, я упаковал три 10-битных целых числа со знаком в целое число Java.

Я могу легко извлечь 10 бит:

int unpacked = packed & 0x3FF;
packed >>= 10;
etc ...

Но теперь мне нужно подписать-расширить верхний бит (бит 9 справа). Есть ли быстрый способ сделать это, прибегнув к тестированию верхнего бита и настройке?

Возможно, есть лучший способ распаковки, который оставляет знак на месте.

2 ответа

Решение

Альтернативой сдвигу в два раза является переворачивание знака и его вычитание:

int unpacked = packed & 0x3FF;
int extended = (unpacked ^ 0x200) - 0x200;

Если знак не был установлен, его переворачивание устанавливает его и вычитает, что сбрасывает его снова.

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

Это имеет некоторые преимущества,

  • Код не зависит от размера целевого целочисленного типа, если unpacked а также extended мы long тогда то же самое сработало бы.
  • XOR и вычитание могут быть немного дешевле, например, на Skylake вы можете выполнять 4 из этих основных операций за цикл, но только 2 смены. Задержка одинакова, и имеет значение только, если доступный ILP в коде высок.
  • Сдвиги на самом деле не алгебраически объединяются, но XOR и вычитание могут. Например, если следующая операция - добавить некоторую константу в extendedзатем эти этапы сложения и "вычитания знака" можно объединить в одну операцию.
int signed = (packed << (32 - 10)) >> (32 - 10);

Как >>> является беззнаковым сдвиг вправо, и >> подписанный сдвиг вправо.

(У меня все еще была "ошибка", спасибо @rghome)

Другие вопросы по тегам