web-dev-qa-db-ja.com

doubleに割り当てられたstatic_cast <float>をdoubleに割り当てて最適化できますか?

私は不必要だと思う機能につまずき、一般的に私を怖がらせます:

float coerceToFloat(double x) {
    volatile float y = static_cast<float>(x);
    return y;
}

これは次のように使用されます:

// double x
double y = coerceToFloat(x);

これはこれを行うことと何か違うことはありますか?:

double y = static_cast<float>(x);

意図は、倍精度を単精度に下げるだけのようです。それは極端なパラノイアから書かれたもののようなにおいがします。

22
Ben

static_cast<float>(x)は、余分な精度を削除してfloatを生成するために必要です。 C++標準では一般に、実装で式の余分な浮動小数点精度を保持できますが、キャストおよび代入演算子によってその精度を削除する必要があります。

より高い精度を使用するためのライセンスは、C++ドラフトN4659条項8、段落13にあります。

浮動オペランドの値と浮動式の結果は、型が必要とするものよりも高い精度と範囲で表現できます。それによってタイプは変更されません。64

脚注64の発言:

キャスト演算子と代入演算子は、8.4、8.2.9、および8.18で説明されているように、引き続き特定の変換を実行する必要があります。

12

@NathanOliverによるコメントのフォローアップ-コンパイラは、オペランドの型が必要とするよりも高い精度で浮動小数点演算を行うことができます。通常、x86では、ハードウェアで最も効率的であるため、すべてを80ビット値として実行します。型の実際の精度に戻す必要があるのは、値が保存されたときだけです。その場合でも、ほとんどのコンパイラーはデフォルトでこの規則に違反する最適化を行います。精度の変更を強制すると浮動小数点演算が遅くなるためです。ほとんどの場合、大丈夫です。なぜなら、余分な精度は有害ではないからです。あなたがスティックラーなら、コマンドラインスイッチを使用してコンパイラにそのストレージルールを強制させることができ、浮動小数点計算が大幅に遅くなることがあります。

その関数では、変数volatileにマークを付けると、コンパイラーにその値の格納を省略できないことを伝えます。これは、格納されている型と一致するように着信値の精度を下げる必要があることを意味します。したがって、これは切り捨てを強制することが期待されます。

そして、いいえ、その関数を呼び出す代わりにキャストを書くことは同じではありません。なぜなら、コンパイラは(非準拠モードで)yへの割り当てをスキップできるためです。値、および切り捨てもスキップできます。目標は、可能な限り高速に浮動小数点計算を実行することであり、中間値の精度を低下させるという微妙なルールに対処する必要があることを覚えておいてください。

ほとんどの場合、中間の切り捨てをスキップしてフラットアウトを実行することは、深刻な浮動小数点アプリケーションに必要なものです。ストレージの切り捨てを必要とするルールは、現実的な要件よりも希望です。

副次的に、Javaは元々、すべての浮動小数点演算が関連する型に必要な正確な精度で行われることを要求していました。 80ビットに。これは、計算を行うため、ナンバークランチャーからの大きな不満に直面しましたmuch遅くなります。Java "strict" fpおよび "non -strict "fp、および深刻な数値演算では非厳密を使用します。つまり、ハードウェアがサポートする限り高速にします。浮動小数点演算を完全に理解している人(つまりnot私を含む)結果の精度の違いに対処する方法を知っています。

11
Pete Becker

一部のコンパイラには、「拡張精度」という概念があり、倍精度では64ビット以上のデータが運ばれます。これにより、IEEE標準に一致しない浮動小数点計算が行われます。

上記のコードは、コンパイラの拡張精度フラグが精度の損失を除去しないようにする試みである可能性があります。このようなフラグは、倍精度浮動小数点値の精度の前提に明示的に違反しています。 volatile変数ではそうしないと思われます。

そのようなキャストが最適化されて許可されるかどうかに関係なく、それは起こります

たとえば、/Ox /fp:fastを使用した32ビット用のMSVCコンパイル(x87を使用)。

_x$ = 8                                       ; size = 8
float uselessCast(double) PROC                         ; uselessCast
        fld     QWORD PTR _x$[esp-4]
        ret     0
float uselessCast(double) ENDP                         ; uselessCast

_y$ = 8                                       ; size = 4
_x$ = 8                                       ; size = 8
float coerceToFloat(double) PROC                   ; coerceToFloat
        fld     QWORD PTR _x$[esp-4]
        fstp    DWORD PTR _y$[esp-4]
        fld     DWORD PTR _y$[esp-4]
        ret     0
float coerceToFloat(double) ENDP 

ここで、uselessCastは以下のとおりで、coerceToFloatは質問のとおりです。

float uselessCast(double x)
{
    return static_cast<float>(x);
}

同様に、GCCと-O3 -ffast-math -m32 -mfpmath=387を含むClang

uselessCast(double):
    fld     QWORD PTR [esp+4]
    ret
coerceToFloat(double):
    sub     esp, 20
    fld     QWORD PTR [esp+24]
    fstp    DWORD PTR [esp+12]
    fld     DWORD PTR [esp+12]
    add     esp, 20
    ret

上記すべてのゴッドボルトリンク

もちろん、/fp:fastまたは-ffast-mathを使用すると、とにかく浮動小数点演算に何も期待すべきではないと主張するかもしれません。

6
harold