这个函数如何通过NOT和AND操作计算浮点数的绝对值?

8
我将尝试理解以下代码片段的工作原理。此程序使用SIMD矢量指令(Intel SSE)来计算4个浮点数的绝对值(因此,基本上是矢量化的“fabs()”函数)。
以下是代码片段:
#include <iostream>
#include "xmmintrin.h"

template <typename T>
struct alignas(16) sse_t
{
    T data[16/sizeof(T)];
};

int main()
{
    sse_t<float> x;
    x.data[0] = -4.;
    x.data[1] = -20.;
    x.data[2] = 15.;
    x.data[3] = -143.;
    __m128 a = _mm_set_ps1(-0.0); // ???
    __m128 xv = _mm_load_ps(x.data);
    xv = _mm_andnot_ps(a,xv); // <-- Computes absolute value
    sse_t<float> result;
    _mm_store_ps(result.data, xv);
    std::cout << "x[0]: " << result.data[0] << std::endl;
    std::cout << "x[1]: " << result.data[1] << std::endl;
    std::cout << "x[2]: " << result.data[2] << std::endl;
    std::cout << "x[3]: " << result.data[3] << std::endl;
}

现在,我知道它是有效的,因为我自己运行程序来测试它。使用g++ 4.8.2编译时,结果如下:

x[0]: 4
x[1]: 20
x[2]: 15
x[3]: 143

我有三个相关问题需要解答:

首先,如何将按位运算符应用于浮点数?如果在普通的C++中尝试这样做,会发现这只适用于整数类型(这是有道理的)。

但更重要的是,其次:

这究竟能起到什么作用呢?为什么使用NOT和AND操作符会对此有所帮助?在Python中对整型进行这样的操作,只会得到预期结果:任何整数与-1(即非0)相与,都可以得到该数字本身,而不改变符号。那么这在此处为什么有效呢?

第三,我注意到,如果我将NAND操作所使用的浮点数(标有三个问号)的值从-0.0更改为0.0,则程序不再给出绝对值。但是-0.0怎么会存在,它又如何帮助实现这一功能呢?

相关参考资料:

Intel Intrinsic指南


这是SSE,数值没有类型 - 您执行的操作决定了如何解释位模式。这只是AND出符号位。 - harold
我不确定我的标签编辑是否是最佳选择。关键是该问题只有在使用IEEE 754浮点表示时才有意义。这是通过使用Visual C++来暗示的。但即使是特定编程语言的使用也大多无关紧要。也许有更好的标签感觉的人可以改进。 - Cheers and hth. - Alf
@Alf 我没有使用Visual C++,否则我为什么要使用gcc编译器呢? 我甚至没有在Windows上使用它,我使用的是Linux。所以,在那里它肯定也能工作。 - Mark Anderson
1个回答

10

-0.01在IEEE-754标准中表示为1000...000。因此,_mm_andnot_ps(-0.0, x)2等价于0111...111 & x。这将强制最高位(即符号位)为0。


1. 在IEEE-754标准中是这样。

2. _mm_andnot_ps指令并不意味着“NAND”;请参见例如http://msdn.microsoft.com/en-us/library/68h7wd02(v=vs.90).aspx


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接