是否有未签名的x87 FILD和SSE CVTSI2SD指令?

tib*_*bbe 5 floating-point assembly sse x87

我想在GHC Haskell编译器中实现C的uint-to- doublecast 的等价物.我们已经实现了int-到- double使用FILDCVTSI2SD.是否有这些操作的无符号版本,或者我应该uint将转换前的最高位置零(从而丢失范围)?

Igo*_*sky 5

正如有人所说,“优秀的艺术家复制;伟大的艺术家窃取”。所以我们可以看看其他编译器作者是如何解决这个问题的。我使用了一个简单的片段:

volatile unsigned int x;
int main()
{
  volatile double  y = x;
  return y;
}
Run Code Online (Sandbox Code Playgroud)

(添加易失性是为了确保编译器不会优化转换)

结果(跳过无关说明):

Visual C++ 2010 cl/Ox (x86)

  __real@41f0000000000000 DQ 041f0000000000000r ; 4.29497e+009

  mov   eax, DWORD PTR ?x@@3IC          ; x
  fild  DWORD PTR ?x@@3IC           ; x
  test  eax, eax
  jns   SHORT $LN4@main
  fadd  QWORD PTR __real@41f0000000000000
$LN4@main:
  fstp  QWORD PTR _y$[esp+8]
Run Code Online (Sandbox Code Playgroud)

因此,基本上编译器会添加一个调整值,以防符号位被设置。

Visual C++ 2010 cl/Ox (x64)

  mov   eax, DWORD PTR ?x@@3IC          ; x
  pxor  xmm0, xmm0
  cvtsi2sd xmm0, rax
  movsdx    QWORD PTR y$[rsp], xmm0
Run Code Online (Sandbox Code Playgroud)

此处无需调整,因为编译器知道这rax将清除符号位。

Visual C++ 2012 cl/Ox

  __xmm@41f00000000000000000000000000000 DB 00H, 00H, 00H, 00H, 00H, 00H, 00H
  DB 00H, 00H, 00H, 00H, 00H, 00H, 00H, 0f0H, 'A'

  mov   eax, DWORD PTR ?x@@3IC          ; x
  movd  xmm0, eax
  cvtdq2pd xmm0, xmm0
  shr   eax, 31                 ; 0000001fH
  addsd xmm0, QWORD PTR __xmm@41f00000000000000000000000000000[eax*8]
  movsd QWORD PTR _y$[esp+8], xmm0
Run Code Online (Sandbox Code Playgroud)

这使用无分支代码添加 0 或根据符号位是否被清除或设置进行魔术调整。


Gun*_*iez 3

您可以利用 IEEE 双精度格式的一些属性,并将无符号值解释为尾数的一部分,同时添加一些精心设计的指数。

Bits 63 62-52     51-0
     S  Exp       Mantissa
     0  1075      20 bits 0, followed by your unsigned int
Run Code Online (Sandbox Code Playgroud)

1075 来自双精度数的 IEEE 指数偏差 (1023) 和尾数的 52 位“移位”量。请注意,尾数前面有一个隐含的“1”,需要稍后将其减去。

所以:

double uint32_to_double(uint32_t x) {
    uint64_t xx = x;
    xx += 1075ULL << 52;         // add the exponent
    double d = *(double*)&xx;    // or use a union to convert
    return d - (1ULL << 52);     // 2 ^^ 52
}
Run Code Online (Sandbox Code Playgroud)

如果您的平台上没有本机 64 位,则使用 SSE 进行整数步骤的版本可能会有所帮助,但这当然取决于情况。

在我的平台上,这会编译为

0000000000000000 <uint32_to_double>:
   0:   48 b8 00 00 00 00 00    movabs $0x4330000000000000,%rax
   7:   00 30 43 
   a:   89 ff                   mov    %edi,%edi
   c:   48 01 f8                add    %rdi,%rax
   f:   c4 e1 f9 6e c0          vmovq  %rax,%xmm0
  14:   c5 fb 5c 05 00 00 00    vsubsd 0x0(%rip),%xmm0,%xmm0 
  1b:   00 
  1c:   c3                      retq
Run Code Online (Sandbox Code Playgroud)

看起来不错。这0x0(%rip)是神奇的双常量,如果内联一些指令,例如高 32 位清零和常量重新加载,将会消失。


归档时间:

查看次数:

894 次

最近记录:

11 年,6 月 前