英语不是我的母语,请原谅我的语法错误
我的机器环境是
AMD 5900x, win10 latest, VS2022 MSVC lateset
以下代码已在我的机器和我同事的机器(与我的非常相似)上通过编译(debug-X86,release-x86 MSVC),但输出不是预期的
#include <intrin.h>
#include <iostream>
#include <xmmintrin.h>
int main(int argc, char* argv[])
{
union
{
float f[2];
__m64 m;
} a = {{10.f, 200.f}};
a.m = _mm_shuffle_pi16(a.m, _MM_SHUFFLE(1, 0, 3, 2));
std::cout << a.f[0] << " " << a.f[1] << std::endl;
std::cout << a.f[0] << " " << a.f[1] << std::endl;
return 0;
}
expected output:
200 10
200 10
ACTUAL output:
-nan(ind) 10
200 10
// note: no math operation undertake between two std::cout
我检查了下面的二进制数据,它们在IEEE 754标准中都是合法的:
拆解很正常:
这真的让我觉得上游有什么BUG吗?或者任何编译环境问题?你遇到过类似的情况吗?需要任何建议或进一步信息吗?预先感谢。
更新1: 相同的代码
Linux kali 6.3.0-kali1-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.3.7-1kali1 (2023-06-29) x86_64 GNU/Linux
gcc (Debian 13.1.0-6) 13.1.0
g++ (Debian 13.1.0-6) 13.1.0
Debian clang version 14.0.6
Target: x86_64-pc-linux-gnu
Thread model: posix
InstalledDir: /usr/bin
compile command:
g++ a.cpp
clang++ -o aaa a.cpp
输出(对于两种编译方法):
200 10
200 10
虽然我不知道在分配给a.m
和打印内容之间到底发生了什么
,但代码本身存在一个错误:
_mm_shuffle_pi16
是一个MMX内在函数,并且您没有调用
_mm_empty
(或
_m_empty
),因此 FPU 状态仍处于 MMX 模式。这会在稍后扰乱 x87 风格的 FPU 指令。您是针对 x86(即 32 位)进行编译的,因此很可能在某些时候使用了 x87 指令。 64 位代码大多不使用 x87 指令,在这种情况下,看起来似乎没有发生什么不好的事情,这可以解释为什么代码似乎可以在 64 位 kali linux 上运行。MMX 此时正在逆计算。您可以添加
_mm_empty
来修复此代码,但您也可以使用 SSE 代替。放弃联合(如果需要,您可以使用内在函数的“cast”系列来进行安全的重新解释,但这里不需要它,因为 SSE 有浮点洗牌),并执行以下操作:
__m128 a = _mm_setr_ps(10.0f, 200.0f, 0.0f, 0.0f);
a = _mm_shuffle_ps(a, a, _MM_SHUFFLE(3, 2, 0, 1));
float test[4];
_mm_storeu_ps(test, a);
std::cout << test[0] << " " << test[1] << std::endl;
历史上曾经存在过 的一些编译器错误,但这不是您在程序中看到的,它一开始就没有
_mm_empty
。