意外输出而 std::cout float32 数据两次,之前已被 _mm_shuffle_pi16 交换

问题描述 投票:0回答:1

英语不是我的母语,请原谅我的语法错误

我的机器环境是

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

c++ assembly ieee-754
1个回答
0
投票

虽然我不知道在分配给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

 的一些编译器错误,但这不是您在程序中看到的,它一开始就没有 
_mm_empty
    

© www.soinside.com 2019 - 2024. All rights reserved.