当前位置: 首页 > 知识库问答 >
问题:

是否存在x87 FILD和SSE CVTSI2SD指令的无符号等价物?

魏雅惠
2023-03-14

我想在GHC Haskell编译器中实现C的uint到double转换的等价物。我们已经使用FILD或CVTSI2SD实现了int到double。这些运算是否有无符号版本,或者我应该在转换之前将uint的最高位置零(从而失去范围)?

共有3个答案

澹台昆
2023-03-14

有更好的办法

__m128d _mm_cvtsu32_sd(__m128i n) {
    const __m128i magic_mask = _mm_set_epi32(0, 0, 0x43300000, 0);
    const __m128d magic_bias = _mm_set_sd(4503599627370496.0);
    return _mm_sub_sd(_mm_castsi128_pd(_mm_or_si128(n, magic_mask)), magic_bias);
}
吴涵育
2023-03-14

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

Bits 63 62-52     51-0
     S  Exp       Mantissa
     0  1075      20 bits 0, followed by your unsigned int

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
}

如果您的平台上没有本机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

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

赵炯
2023-03-14

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

volatile unsigned int x;
int main()
{
  volatile double  y = x;
  return y;
}

(添加挥发物以确保编译器不会优化转换)

结果(跳过不相关的说明):

  __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]

所以基本上编译器是在设置符号位的情况下添加一个调整值。

  mov   eax, DWORD PTR ?x@@3IC          ; x
  pxor  xmm0, xmm0
  cvtsi2sd xmm0, rax
  movsdx    QWORD PTR y$[rsp], xmm0

这里不需要调整,因为编译器知道rax将清除符号位。

  __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

这使用无分支代码添加0或魔术调整,这取决于符号位是被清除还是被设置。

 类似资料:
  • 不情愿的kotlin新来的。 是否存在kotlin等价的?:java运算符? 在Kotlin中寻找这样的写法:

  • 就像Java 8中的(在某种程度上)与Scala的类型等价一样,是否存在与Scala的等价的类型?

  • 我认为2补码的全部意义在于可以以相同的方式实现有符号和无符号数字的操作。维基百科甚至特别将乘法列为受益的操作之一。那么为什么x86对每个都有单独的说明,和?x86-64仍然如此吗?

  • Java7引入了MethodHandle类,用于动态执行给定类的方法。据我所知,在JDK的Android端口上没有这样的东西。除了使用标准的反射类之外,是否有任何解决方法可以用来绕过这个问题?

  • 我最近回答了一个问题,关于当和是指向不同对象/数组的指针时,在C中执行

  • 添加日志语句后,getViewType似乎只接收到0的位置。这导致onCreateView只加载一个布局: 加载CommentFragment的片段转换: 片段的OnCreateView: 适配器代码(感谢hister让我开始): 使用上面的代码,只显示头布局,因为viewType始终为0。看起来是这样的。如果我强制另一个布局,它看起来如下所示: