2012-02-05 87 views
3

x86沒有SSE指令將無符號的 int32轉換爲浮點數。實現這個目標的最有效的指令順序是什麼?將uint32的向量轉換爲float向量的最有效方法?

編輯: 爲了澄清,我想要做的以下標量操作的載體序列:

unsigned int x = ... 
float res = (float)x; 

EDIT2:這裏是做一個標量變換的幼稚算法。

unsigned int x = ... 
float bias = 0.f; 
if (x > 0x7fffffff) { 
    bias = (float)0x80000000; 
    x -= 0x80000000; 
} 
res = signed_convert(x) + bias; 
+0

你的意思是截斷/舍入/ ...?你能舉一個最簡單的例子來說明所需的輸入/輸出嗎? – 2012-02-05 18:20:15

+0

添加編輯來澄清 – 2012-02-05 18:48:07

+0

我很困惑,你是否想將'int'轉換爲'float'或'float'爲'int'或兩者?你能否糾正問題的標題和/或身體,使其不那麼模糊? – 2012-02-05 19:08:43

回答

3

你天真的標量的算法不提供正確圓潤轉換 - 它將從某些輸入雙舍入受損。例如:如果x0x88000081,則正確舍入的轉換爲浮點數的結果爲2281701632.0f,但您的標量算法將返回2281701376.0f

關閉我的頭頂,你可以做一個正確的轉換如下(我說的,這是從我的頭頂,所以很可能可以節約的地方的指令):

movdqa xmm1, xmm0 // make a copy of x 
psrld xmm0, 16  // high 16 bits of x 
pand  xmm1, [mask] // low 16 bits of x 
orps  xmm0, [onep39] // float(2^39 + high 16 bits of x) 
cvtdq2ps xmm1, xmm1  // float(low 16 bits of x) 
subps xmm0, [onep39] // float(high 16 bits of x) 
addps xmm0, xmm1 // float(x) 

其中常數具有下列值:

mask: 0000ffff 0000ffff 0000ffff 0000ffff 
onep39: 53000000 53000000 53000000 53000000 

這樣做是分別高和各車道的低半部轉換爲浮點,再加入這些轉換後的值一起。因爲每一半隻有16位寬,所以轉換爲浮點數不會導致舍入。只有在添加兩半時纔會發生舍入;因爲添加是一個正確的四捨五入操作,整個轉換正確舍入。

相比之下,您的樸素實現首先將低31位轉換爲浮點數,這會導致舍入,然後有條件地將2^31添加到該結果中,這可能會導致第二次舍入。任何時候,如果您在轉換中有兩個單獨的舍入點,除非您非常小心它們的發生方式,否則不應指望結果正確舍入。

+0

你能解釋一下你的答案嗎? – 2012-02-05 20:39:10

+0

@zr:你對此感到困惑嗎? – 2012-02-05 20:39:32

+0

乍一看,我不明白數學。爲什麼你的食譜給出正確答案?不是我說這是不正確的... – 2012-02-05 20:41:09

0

如果我正確地理解了你,你想把32位浮點數轉換爲無符號整數,簡單地扔掉浮點數的符號。在這種情況下,這應該工作(NASM語法):

section .data 
mask : dd 0x7fffffff, 0x7fffffff, 0x7fffffff, 0x7fffffff 

section .text 
to_unsigned: 
; assume the floats are in xmm0 at this point 
pand xmm0, [mask] ; mask out the sign bits 
cvtps2dq xmm1, xmm0 ; xmm1 has the unsigned integers at this point 
+0

謝謝但您的解決方案將不會給以下輸入向量[0x80000000,0x80000000,0x80000000,0x80000000] – 2012-02-05 18:46:49

+0

正確的結果這些只是負零。它們通過'cvtps2dq'指令轉換爲正常的零。我不明白這是不正確的,看到如何無法表示二進制補碼中的負數零。 – 2012-02-05 18:52:10

+0

我誤解了你,因爲我以爲你想將浮點數轉換爲無符號整數,而你想要的是以相反的方式進行轉換。不幸的是,在那種情況下,使用SSE沒有簡單的方法。SSE支持的唯一轉換指令假定整數是有符號的,即最重要的位是符號位。我會說,你無能爲力。 – 2012-02-05 19:02:25

1

這是根據從舊但有用蘋果的AltiVec-SSE遷移文檔不幸的是,現在不再在http://developer.apple.com一個例子:

inline __m128 _mm_ctf_epu32(const __m128i v) 
{ 
    const __m128 two16 = _mm_set1_ps(0x1.0p16f); 

    // Avoid double rounding by doing two exact conversions 
    // of high and low 16-bit segments 
    const __m128i hi = _mm_srli_epi32((__m128i)v, 16); 
    const __m128i lo = _mm_srli_epi32(_mm_slli_epi32((__m128i)v, 16), 16); 
    const __m128 fHi = _mm_mul_ps(_mm_cvtepi32_ps(hi), two16); 
    const __m128 fLo = _mm_cvtepi32_ps(lo); 

    // do single rounding according to current rounding mode 
    return _mm_add_ps(fHi, fLo); 
} 
+0

這也是一個很好的答案。我想知道在精度和性能方面它與Stephen Canon的解決方案相比如何。 – 2012-02-06 11:21:54

+0

另一種解決方案看起來不錯,但上面的代碼具有經過測試的優點,並且使用了內部函數,這使得它更便於攜帶。雖然性能可能沒有太大差別。 – 2012-02-06 11:31:18

相關問題