В чем причина побитовых логических операций с плавающей точкой AVX?

AVX допускает побитовые логические операции, такие как и / или над типом данных с плавающей запятой __m256 и __m256d.

Тем не менее, C ++ не допускает побитовых операций над числами с плавающей точкой и удваивается, разумно. Если я прав, нет гарантии на внутреннее представление чисел с плавающей точкой, будет ли компилятор использовать IEEE754 или нет, поэтому программист не может быть уверен в том, как будут выглядеть биты с плавающей точкой.

Рассмотрим этот пример:

#include <immintrin.h>
#include <iostream>
#include <limits>
#include <cassert>

int main() {

float x[8] = {1,2,3,4,5,6,7,8};
float mask[8] = {-1,0,0,-1,0,-1,0,0};
float x_masked[8];

assert(std::numeric_limits<float>::is_iec559);

__m256 x_ = _mm256_load_ps(x);
__m256 mask_ = _mm256_load_ps(mask);

__m256 x_masked_ = _mm256_and_ps(x_,mask_);

_mm256_store_ps(x_masked,x_masked_);

for(int i = 0; i < 8; i++)
std::cout << x_masked[i] << " ";

return 0;
}

Предполагая, что используется IEEE754, поскольку представление -1 равно 0xffffffff, я ожидаю, что результат будет

1,0,0,4,0,6,0,0

пока это вместо

1 0 0 1.17549e-38 0 1.17549e-38 0 0

Следовательно, мое предположение о внутреннем представлении, вероятно, было неверным (или я допустил какую-то глупую ошибку).

Таким образом, вопрос заключается в следующем: есть ли способ, которым я могу использовать логические числа с плавающей запятой и быть уверенным в том факте, что результат будет иметь смысл?

4

Решение

Если вы используете встроенные функции AVX, то вы знаете, что используете плавающие объекты IEEE754, потому что это то, что делает AVX.

Некоторые из побитовых операций над числами с плавающей запятой, которые имеют смысл:

  • выбирая, как в ответе Йенса, хотя на SSE4.1 мы имеем blendvps и его родственники, чтобы сделать это в одной инструкции
  • абсолютное значение (скрыть знак)
  • отрицать (xor с -0.0f)
  • знак передачи
  • извлечение показателя степени (редко)

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

8

Другие решения

Причина в том, что могут быть штрафы за переключение между доменами исполнительных блоков двухконтурности задержка-когда-коммутационный-исполнение-блок-домены
а также почему-Do-некоторые-SSE-МЫ-инструкция, указать, что: они въезд с плавающей точкой стоимости. В этом случае переключение с исполнительного блока AVX с плавающей запятой на целочисленный исполнительный блок AVX.

Например, допустим, вы хотите сравнить регистры AVX с плавающей запятой x а также y

z = _mm256_cmp_ps(x, y, 1);

Регистр AVX z содержит логические целочисленные значения (0 или -1), которые затем можно логически И, используя _mm256_and_ps или с _mm256_and_si256 если ты хотел. Но _mm256_and_ps остается в том же исполнительном блоке и _mm256_and_si256 переключает блоки, которые могут вызвать задержку байпаса.

Редактировать: в отношении побитовых операторов на числах с плавающей точкой в ​​C ++ это, конечно, возможно и иногда полезно. Вот несколько простых примеров.

union {
float f;
int i;
} u;
u.i ^= 0x80000000; // flip sign bit of u.f
u.i &= 0x7FFFFFFF; // set sign bit to zero //take absolute value
4

Программист может быть совершенно уверен, как представлены числа с плавающей запятой одинарной точности. Как реализованы функции — это другая история. Я использовал побитовые операции для реализации поплавков с половинной точностью, соответствующих IEEE-754. Я также использовал операции по удалению веток еще в 2003 году — до того, как IBM подала на это патент.

static inline __m128 _mm_sel_ps(__m128 a, __m128 b, __m128 mask ) {
b = _mm_and_ps( b, mask );
a = _mm_andnot_ps( mask, a );
return _mm_or_ps( a, b );
}

Этот пример демонстрирует, как удалить ветвь с плавающей запятой, используя SSE2. То же самое может быть достигнуто с помощью AVX. Если вы попытаетесь (той же техникой) удалить ветви с помощью скаляров, вы не получите никакой производительности из-за переключения контекста (относится к x86 — не относится к ARM, где у вас есть операция fpsel)

2