在将uint64转换为double时:为什么进行一次右移操作后代码就更简单了?

9

为什么AsDouble1AsDouble0更加简单明了?

// 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; }

可以在此处获取代码:https://godbolt.org/z/dKc6Pe6M1


1
请注意,如果您只想查看汇编代码,则无需在Godbolt上包含main()函数。此外,与此有些相关的是是否存在x87 FILD和SSE CVTSI2SD指令的无符号等效项?,但这是相反的方向。如何使用SSE / AVX高效执行double / int64转换?涵盖了打包转换。 - Peter Cordes
什么让 `uint64_t` 这么难?(从 `float` 转换汇编代码) 是关于编译器生成的 (uint64_t)float 代码的专题。 - Peter Cordes
@PeterCordes 关于 main(),这是我其他测试的产物。 - Etienne M
2个回答

8

x86有一条指令可以在带符号整数和浮点数之间进行转换。AVX512支持无符号整数转换(我想是这样的),但大多数编译器默认情况下不会使用它。如果你将uint64_t向右移动一次,符号位就会被删除,这样你就可以将其解释为带符号整数并获得相同的结果。


2
正确的,vcvtsd2usi 是AVX-512中的新功能,还包括将打包转换为无符号32位和64位整数(64位打包转换也是新功能)。 - Peter Cordes
2
@Peter,将无符号整数转换为双精度浮点数不应该是vcvtusi2sd吗?但我想我们都知道你的意思。 :-) - Adrian Mole
1
@AdrianMole:哎呀,是的,我把方向搞反了,在问题下面的评论里也是。 - Peter Cordes

6
cvtsi2sd指令的源操作数为带符号整数(32位或64位)。但是,您的函数采用无符号参数。
因此,在第一种情况下,编译器无法直接使用cvtsi2sd指令,因为给定参数中的值可能无法表示为具有相同大小的有符号整数- 因此它会生成执行转换到double的代码"长路"(但是安全)。
然而,在您的第二个函数中,初始的右移一位操作保证了符号位将被清除;因此,无论该结果值是作为带符号还是无符号来解释,都将是相同的...所以编译器可以安全地使用那个(修改后的)值作为cvtsi2sd操作的源。

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