我想实现C'S相当于uint
至- double
在GHC Haskell编译演员。 我们已经实现了int
至- double
用FILD
或CVTSI2SD
。 有这些操作的无符号的或我应该零出的最高位uint
转换(从而失去范围)之前?
Answer 1:
你可以利用一些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位调零和恒定重载某些指令会消失。
Answer 2:
有一个更好的方法
__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);
}
Answer 3:
正如有人所说,“好的艺术家复制,伟大的艺术家偷窃。” 所以,我们就可以检查其他编译器编写者是如何解决这个问题。 我用一个简单的片断:
volatile unsigned int x;
int main()
{
volatile double y = x;
return y;
}
(加入的挥发物,以确保编译器不优化了转换)
结果(无关指令跳过):
视觉C ++ 2010 CL /牛(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]
因此,基本上,编译器是增加的情况下的符号位被设定的调整值。
视觉C ++ 2010 CL /牛(x64)的
mov eax, DWORD PTR ?x@@3IC ; x
pxor xmm0, xmm0
cvtsi2sd xmm0, rax
movsdx QWORD PTR y$[rsp], xmm0
没有必要在这里调整,因为编译器知道rax
将有符号位清零。
视觉C ++ 2012 CL /牛
__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或魔法调整取决于符号位是否被清除或设置。
Answer 4:
我们已经在使用FILD实现INT-到双...
有没有无符号这些操作的版本
如果你想准确的x87 FILD操作码的使用,只是转移到UINT64 uint63(DIV 2),然后通过2 MUL回来,但已经为双,所以UINT64的x87到双转换需要开销一个FMUL执行。
的例子:0xFFFFFFFFFFFFFFFFU - > + 1.8446744073709551e + 0019
它无法张贴在严格的形式规则的代码示例。 我会稍后再试。
//inline
double u64_to_d(unsigned _int64 v){
//volatile double res;
volatile unsigned int tmp=2;
_asm{
fild dword ptr tmp
//v>>=1;
shr dword ptr v+4, 1
rcr dword ptr v, 1
fild qword ptr v
//save lsb
//mov byte ptr tmp, 0
//rcl byte ptr tmp, 1
//res=tmp+res*2;
fmulp st(1),st
//fild dword ptr tmp
//faddp st(1),st
//fstp qword ptr res
}
//return res;
//fld qword ptr res
}
VC产生86输出
//inline
double u64_to_d(unsigned _int64 v){
55 push ebp
8B EC mov ebp,esp
81 EC 04 00 00 00 sub esp,04h
//volatile double res;
volatile unsigned int tmp=2;
C7 45 FC 02 00 00 00 mov dword ptr [tmp], 2
_asm{
fild dword ptr tmp
DB 45 FC fild dword ptr [tmp]
//v>>=1;
shr dword ptr v+4, 1
D1 6D 0C shr dword ptr [ebp+0Ch],1
rcr dword ptr v, 1
D1 5D 08 rcr dword ptr [v],1
fild qword ptr v
DF 6D 08 fild qword ptr [v]
//save lsb
// mov byte ptr [tmp], 0
//C6 45 FC 00 mov byte ptr [tmp], 0
// rcl byte ptr tmp, 1
//D0 55 FC rcl byte ptr [tmp],1
//res=tmp+res*2;
fmulp st(1),st
DE C9 fmulp st(1),st
// fild dword ptr tmp
//DB 45 FC fild dword ptr [tmp]
// faddp st(1),st
//DE C1 faddp st(1),st
//fstp qword ptr res
//fstp qword ptr [res]
}
//return res;
//fld qword ptr [res]
8B E5 mov esp,ebp
5D pop ebp
C3 ret
}
我的评论(可能是我手工删除文本文件中的所有incorrected ASCII字符)。
Answer 5:
如果我正确理解你,你应该能够在您的32位单元移动到一个临时区域堆栈,零出下一个DWORD,然后用FILD四字PTR加载现在的64位无符号整数作为双。