【问题标题】:Which is the reason for avx floating point bitwise logical operations?avx浮点按位逻辑运算的原因是什么?
【发布时间】:2014-09-16 14:06:05
【问题描述】:

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

因此我对内部表示的假设可能是错误的(或者我犯了一些愚蠢的错误)。

所以问题是:有没有一种方法可以使用浮点逻辑并且对结果有意义的事实是安全的?

【问题讨论】:

  • 在 IEEE754 中,-1 不是 0xffffffff,而是 0xbf800000。
  • @genisage,当您与例如_mm256_cmp_ps(x, y, 1) 它返回 -1 = 0xffffffff 而不是 0xbf800000。浮点 AVX 位运算符的行为与整数 AVX 运算符一样,只是它们在浮点执行 uni 而不是整数 1 中运行。
  • 可以在 C++ 中对浮点数进行位运算符。看我的回答。这是一个相当好的假设,即 x86-64 代码中的所有浮点运算都将使用 IEEE754。
  • 当你和_mm256_cmp_ps比较时,返回类型是__m256而不是__m256i。 False 是 0x00000000,True 是 0xFFFFFFFF,因此根据您将真实结果视为整数还是浮点数,答案是 -1 或 1.7549e-38。

标签: c++ simd avx avx2


【解决方案1】:

如果您使用的是 AVX 内在函数,那么您知道您使用的是 IEEE754 浮点数,因为这就是 AVX 的作用。

一些有意义的浮点按位操作是

  • 选择,就像 Jens 的回答一样,尽管从 SSE4.1 开始,我们有 blendvps 及其亲属在一条指令中执行此操作
  • 绝对值(屏蔽掉符号)
  • 取反(与 -0.0f 异或)
  • 转让标志
  • 提取指数(罕见)

主要是为了操作符号,或者选择性地将整个浮点数归零,而不是为了处理指数或有效数字的各个位 - 你可以这样做,但它很少有用。

【讨论】:

  • 不错的答案。到目前为止,与 SSE2 相比,我在使用 avx 方面收获甚微,但 blendvps 操作无疑是使用 avx 的一个原因。
  • 您可以使用 AVX2 整数运算对浮点值执行所有这些操作。问题是,当您可以使用 mm256_and_si256 时,为什么还需要 _mm256_and_ps
  • @Zboson 也许。不过,这不是这个问题对我的解读。标题看起来像是要问这个问题,但问题的主体似乎更关心浮点数的按位运算首先是如何有意义的。
  • 我重读了这个问题,我明白了你的意思。如果 OP 只是想知道为什么按位运算对 SIMD 浮点数有意义,那么答案与询问为什么它对标量有用是一样的。这远没有那么有趣。
  • 我提出问题的主要原因是我不确定 AVX 使用 IEEE754 的事实。对浮点进行按位运算的另一个原因是指令 _mm256_mask_i32gather_ps 需要浮点掩码。我发现使用按位运算更容易创建掩码(当然总是只是改变符号位的问题)
【解决方案2】:

原因是在执行单元bypass-delays-when-switching-execution-unit-domains的域之间切换可能会有惩罚 和why-do-some-sse-mov-instructions-specify-that-they-move-floating-point-values。在这种情况下,从浮点 AVX 执行单元切换到整数执行 AVX 单元。

例如,假设您要比较浮点 AVX 寄存器 xy

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

【讨论】:

    【解决方案3】:

    程序员可以完全确定单精度浮点的表示方式。如何实现功能是另一回事。我利用按位运算来实现符合 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)

    【讨论】:

    • IBM 曾尝试基于具有 and/andnot/or? 的掩码申请专利混合?哈哈。在没有 SSE4.1 blendvps 的情况下自动矢量化三元组时,像 gcc 和 clang 这样的编译器经常使用这种模式,所以如果 IBM 的专利曾经相关,那么现在就不是了。
    猜你喜欢
    • 2012-07-20
    • 2011-07-17
    • 2013-01-21
    • 2010-10-29
    • 1970-01-01
    • 1970-01-01
    • 2015-05-25
    • 2014-07-01
    • 2023-04-06
    相关资源
    最近更新 更多