关于 uint64 到 double 的转换:为什么右移 1 后代码更简单?

Eti*_*e M 9 c++ assembly sse x86-64 floating-point-conversion

为什么AsDouble1比 更简单AsDouble0

// AsDouble0(unsigned long):                          # @AsDouble0(unsigned long)
//         movq    xmm1, rdi
//         punpckldq       xmm1, xmmword ptr [rip + .LCPI0_0] # xmm1 = xmm1[0],mem[0],xmm1[1],mem[1]
//         subpd   xmm1, xmmword ptr [rip + .LCPI0_1]
//         movapd  xmm0, xmm1
//         unpckhpd        xmm0, xmm1                      # xmm0 = xmm0[1],xmm1[1]
//         addsd   xmm0, xmm1
//         addsd   xmm0, xmm0
//         ret
double AsDouble0(uint64_t x) { return x * 2.0; }

// AsDouble1(unsigned long):                          # @AsDouble1(unsigned long)
//         shr     rdi
//         cvtsi2sd        xmm0, rdi
//         addsd   xmm0, xmm0
//         ret
double AsDouble1(uint64_t x) { return (x >> 1) * 2.0; }
Run Code Online (Sandbox Code Playgroud)

代码位于: https: //godbolt.org/z/dKc6Pe6M1

xiv*_*r77 8

x86 有一条在有符号整数和浮点数之间进行转换的指令。(我认为)AVX512 支持无符号整数转换,大多数编译器默认情况下并不支持这种转换。如果右移一次uint64_t,符号位就会消失,因此您可以将其解释为有符号整数并得到相同的结果。

  • 正确的是,[`vcvtsd2usi`](https://www.felixcloutier.com/x86/vcvtsd2usi) 是 AVX-512 的新功能,以及到无符号 32 位和 64 位整数的打包转换。(打包转换为 64 位也是新的)。 (2认同)
  • @Peter将一个unsinged int 转换为double 不是“vcvtusi2sd”吗?但我想我们都知道你的意思。:-) (2认同)

Adr*_*ica 6

cvtsi2sd指令采用有符号整数(32 位或 64 位宽)作为其源操作数。但是,您的函数采用无符号参数。

\n

因此,在第一种情况下,编译器无法直接使用该cvtsi2sd指令,因为给定参数中的值可能无法表示为相同大小的有符号整数 \xe2\x80\x93,因此它生成的代码将转换为double“很长一段路”(但安全)。

\n

但是,在第二个函数中,初始右移一位可以保证符号位清晰;因此,无论将其解释为有符号还是无符号 \xe2\x80\xa6,结果值都将是相同的,因此编译器可以安全地使用该(修改后的)值作为操作的源cvtsi2sd

\n