2015-09-21 16 views
7

私はsize_t s=(size_t)floorf(f);のようなものを計算する必要がある状況にあります。つまり、引数は浮動小数点ですが、整数値(floorf(f)は正確に表現するのに十分小さいと仮定します)を持ちます。これを最適化しながら、面白いことを発見しました。`uint64_t`はなぜ難しいですか? (変換アセンブリを `float`から)

floatから整数(GCC 5.2.0 -O3)への変換があります。わかりやすくするために、与えられた変換はテスト関数の戻り値です。

はここint32_t x=(int32_t)fです:

cvttss2si eax, xmm0 
    ret 

はここuint32_t x=(uint32_t)fです:

cvttss2si rax, xmm0 
    ret 

ここint64_t x=(int64_t)fです:

cvttss2si rax, xmm0 
    ret 

最終、ここuint64_t x=(uint64_t)f;です:

ucomiss xmm0, DWORD PTR .LC2[rip] 
    jnb .L4 
    cvttss2si rax, xmm0 
    ret 
.L4: 
    subss xmm0, DWORD PTR .LC2[rip] 
    movabs rdx, -9223372036854775808 
    cvttss2si rax, xmm0 
    xor rax, rdx 
    ret 

.LC2: 
    .long 1593835520 

この最後のものは他のものよりずっと複雑です。さらに、ClangとMSVCも同様に動作します。あなたの便宜のために、私は擬似C言語に翻訳しました:

float lc2 = (float)(/* 2^63 - 1 */); 
if (f<lc2) { 
    return (uint64_t)f; 
} else { 
    f -= lc2; 
    uint64_t temp = (uint64_t)f; 
    temp ^= /* 2^63 */; //Toggle highest bit 
    return temp; 
} 

これは、最初のオーバーフローモード64を正しく計算しようとしているようです。ちなみに、私は、the documentation for cvttss2siは、オーバーフローが発生した場合(2^32ではなく2^64)、「不定の整数値(80000000H)が返されます。

私の質問:

  1. これは本当に何をしている、となぜか?
  2. 他の整数型でも同様のことが行われなかったのはなぜですか?
  3. 同様のコード(出力行3と4のみ)を生成するように変換を変更するにはどうしたらいいですか(値は正確に表現可能であるとします)? cvttss2si以来
+0

このブログの投稿とこのコメントは、あなたの質問に関連しています:http://blog.frama-c.com/index.php?post/2013/10/09/Overflow-float-integer#c379 –

答えて

8

が署名した変換を行い、それが実際に彼らは、符号なしのための範囲内にあるとき、範囲外であることをインターバル[2^63, 2^64)中の数字を検討します。したがって、この場合は検出され、フロートの下半分にマッピングされ、変換後に補正が適用されます。

uint32_t変換では、引き続きuint32_tの全範囲で機能する64ビットの宛先が使用され、さらに、呼び出し規約に従って結果の下位32ビットを使用することで暗黙的に切り捨てられます。

余分なコードを避けるという点では、あなたの入力が上記の範囲に入るかどうかによって異なります。可能であれば、その周りに道はありません。さもなければ、署名されてから署名されていないものへの最初の二重キャストは機能します。 (uint64_t)(int64_t)f

関連する問題