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
因此,我对内部表征的假设可能是错误的(或者我犯了一些愚蠢的错误)。
所以问题是:有没有一种方法可以使用浮点逻辑并确保结果有意义?
答案 0 :(得分:8)
如果你正在使用AVX内在函数,那么你就知道你正在使用IEEE754浮点数,因为那是AVX所做的。
对浮点数的一些有意义的操作是有意义的
blendvps
及其亲戚在一条指令中做到这一点主要用于操纵符号,或者选择性地清除整个浮点数,而不是用指数或有效数字的各个位置进行捣乱 - 你可以做到,但它很少有用
答案 1 :(得分:4)
原因是在执行单位的域之间切换可能会受到惩罚bypass-delays-when-switching-execution-unit-domains 和why-do-some-sse-mov-instructions-specify-that-they-move-floating-point-values。在这种情况下,从浮点AVX执行单元切换到整数执行AVX单元。
例如,假设您要比较浮点AVX寄存器x
和y
z = _mm256_cmp_ps(x, y, 1);
AVX寄存器z
包含布尔整数值(0或-1),如果需要,您可以使用_mm256_and_ps
或_mm256_and_si256
进行逻辑AND。但是_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
答案 2 :(得分:2)
程序员可以完全确定如何表示单精度浮点。如何实现功能是另一个故事。我已经使用按位运算来实现符合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 - 不适用于具有fpsel操作的ARM)