13

そのため、このトピックについて検索しましたが、実際に関連するものは何も見つかりませんでした。

この単純なコードの背後にあるアセンブリを見ようとしました:

int main(int argc, char *argv[])
{
    double d = 1.0;
    float f = static_cast<float>(d);

    system("PAUSE");
    return 0;
}

これは(Visual Studio 2012で):

    15:     double d = 1.0;
000000013FD7C16D  movsd       xmm0,mmword ptr [__real@3ff0000000000000 (013FD91AB0h)]  
000000013FD7C175  movsd       mmword ptr [d],xmm0  
    16:     float f = static_cast<float>(d);
000000013FD7C17B  cvtsd2ss    xmm0,mmword ptr [d]  
000000013FD7C181  movss       dword ptr [f],xmm0

私は組み立てに慣れていませんが、とにかくそれを分析しようとしました。したがって、最初の 2 行は、倍精度値3ff0000000000000をレジスタに移動し、次にレジスタの内容を d のメモリ アドレスに移動するように見えます。

次に、次の行が何をするのか正確にはわかりません。このcvtsd2ss操作は、倍精度浮動小数点値を単精度浮動小数点値に変換する命令のようですが、この命令が実際に何をするのかわかりませんでした。(その後、変換された値は f のメモリ空間に移動されます)。

だから私の質問は、この命令によって実際にこの変換がどのように行われるのですか? C++ キャストが他の型で最も近い値を生成することは知っていますが、それ以外は、実行された実際の操作についてはわかりません...

4

1 に答える 1

15

このcvtsd2ss命令は、FPU の丸めモードを使用して変換を行います。デフォルトの丸めモードは、最近接偶数への丸めです。

アルゴリズムに従うには、 IEEE 754-1985 Wikipedia ページの情報、特にレイアウトを表す図を覚えておくと役立ちます。

最初に、ターゲットの指数floatが計算されます。double型の範囲は よりも広いfloatため、結果は0.0f非常に小さい の場合 (または非正規化) になるdoubleか、非常に大きな double の場合は無限値になる可能性があります。

法線が法線doubleに変換される通常の場合float(大まかに言えば、バイアスのない指数がdouble単精度表現の 8 ビットで表現できる場合)、変換先仮数の最初の 23 ビットは、変換先と同じように開始されます。元の数値の 52 ビット仮数の最上位。

次に、丸めの問題があります。

  • 残りのビットが 未満10..0の場合、ターゲット仮数はそのまま残されます。

  • 残りのビットが より大きい場合10..0、ターゲット仮数がインクリメントされます。インクリメントするとオーバーフローする場合 (既に であるため1..1)、キャリーは指数ビットに伝搬されます。これは、IEEE 754 レイアウトが慎重に設計されているため、正しい結果を生成します。

  • 残ったビットがちょうど10..0である場合、doubleは 2 つの のちょうど中間floatです。これら 2 つの選択肢のうち、最後のビット0(「偶数」) を持つものが選択されます。

このステップの後、ターゲット仮数floatは元の に最も近いものに対応しdoubleます。

有向丸めモードは単純です。ターゲットがデノーマルである場合は、floatもう少し複雑です (「二重の丸め」を避けるように注意する必要があります)。

于 2013-05-24T14:45:26.350 に答える