Какой способ лучше получить более низкие 32 бита из 64-битного целого числа

Я обнаружил, что в некоторых ответах они рекомендовали использовать

lower = (some_var << 32) >> 32;

Но я тестировал и нашел следующее быстрее:

lower = some_var & 0xffffffff;

Итак, что лучше? Является ли бывший безопасным в некоторых случаях или быстрее после оптимизации компилятора?

Ответ 1

Маскировка с помощью & лучше:

  • & является надежным для подписанных и неподписанных some_var, в то время как правильное отрицательное смещение числа приводит к результату, определенному реализацией:

Значение E1 → E2 - это позиции E1 с правым сдвигом E1. [...] Если E1 имеет подписанный тип и отрицательное значение, результирующее значение определяется реализацией.

  • для каждого процессора, который я когда-либо знал (Z80, 6502C, x86, 68000, UltraSparc), побитовое - И является одной инструкцией процессора и занимает один такт... это крайне маловероятно, чтобы быть медленнее или принимать больше байтов машинный код, чем упомянутый вами подход к смещению бит, хотя компилятор может оптимизировать его побитовое И вообще.

Единственным недостатком маскировки является то, что относительно легко случайно иметь 7 или 9 F s, тогда как опечатка в 32 очевидна: существуют другие способы генерации значения маскировки, например, (1LL<<32)-1, или хакерский, но как-то изящный uint32_t(-1).

Конечно, если lower - uint32_t и some_var uint64_t, вы можете просто позволить преобразование быть неявным, поэтому оптимизатору даже не нужно реализовать побитовое - и его можно удалить перед назначением, но это может дать вам предупреждение компилятора, которое вы можете отключить ala...

uint32_t lower = static_cast<uint32_t>(some_var);

Маскирование в основном полезно при назначении другому uint64_t или когда маска не для всех 32 младших значащих бит.

Ответ 2

Маскировка с И лучше, если она не зависит от знака знака.

Но самый эффективный способ взять младший 32 бит - назначить ему 32-битную переменную.

uint64_t u = 0x1122334455667788;
uint32_t n;

n = static_cast<uint32_t>(u);  // 0x55667788

Разница в бит-мудном И заключается в том, что процессор просто берет нижнюю часть без какой-либо логической операции.

Если у вас 32-разрядный процессор, он просто игнорирует верхнее значение, сохраненное во втором регистре или месте памяти.

Если у вас 64-разрядный процессор, у него есть одна команда для расширения (без знака) 32-битного значения до 64-битного значения.

Ответ 3

Хороший оптимизатор будет генерировать один и тот же код в обоих случаях. Для меня это самый прямой метод: lower = some_var & 0xffffffff; Другая форма может создавать ненужные сдвиги.

Иногда я использую объединение для перекрытия переменных, когда хочу быть абсолютно уверенным, что компилятор не испортил вещи.

Например:

typedef union {
    int64 QWORD;
    int32 DWORD[2];
} overlapper64;

overlapper someVariable;

Затем используйте его как:

someVariable.QWORD;

int32 myVar32 = someVariable.DWORD[0];

В зависимости от платформы/компилятора порядок, в котором происходит перекрытие, может отличаться. Обязательно проверьте его на своей конкретной платформе. В C я использую кучу специфических для платформы #ifdefs для автоматического управления заказом.

Ответ 4

Добавляя к тому, что говорили другие, на основе компилятора, который вы используете, второй вариант может быть быстрее, потому что если не оптимизирован, первый вариант реализован как 2 инструкции процессора, а второй вариант - одна команда cpu, Это может быть причиной того, что вы наблюдаете повышение производительности, используя второй вариант.

Ответ 5

Я думаю, что оба одинаково хороши. Однако использование побитового оператора было бы лучше (не уверен, есть ли разница в производительности), поскольку стандарт говорит:

6.5.7 Операторы побитового сдвига

4 Результат E1 < E2 - E1 сдвинутые слева позиции E2; освобождено биты заполняются нулями. Если E1 имеет беззнаковый тип, значение результатом является E1 × 2E2, приведенный по модулю один больше максимального значения представимые в типе результата. Если E1 имеет подписанный тип и неотрицательный значение и E1 × 2E2 представимо в типе результата, то это итоговое значение; в противном случае поведение undefined.