36

IEEE-754準拠を仮定すると、double を介して転送されたときに float が保持されることが保証されますか?

つまり、次のアサーションは常に満たされるのでしょうか?

int main()
{
    float f = some_random_float();
    assert(f == (float)(double)f);
}

NaNfや Infinity など、IEEE で定義された特別な値を取得できるとします。

IEEE によると、assert は満たされるが、double を介した転送後に正確なビットレベル表現が保持されない場合はありますか?

コード スニペットは、C と C++ の両方で有効です。

4

3 に答える 3

31

IEEE を想定する必要さえありません。C89 は 3.1.2.5 で次のように述べています。

タイプの値のセットは、タイプfloatの値のセットのサブセットですdouble

そして、他のすべての C および C++ 標準は、同等のことを述べています。私の知る限り、NaN と無限大は「型の値」ですが、floatオペランドとして使用する場合は特殊なルールが適用されます。

float -> double -> float 変換がfloat(一般に) 元の値を復元するという事実は、変換先の型で表現できる場合、数値変換はすべて値を保持するという事実からわかります。

ビットレベルの表現は少し異なります。float2 つの異なるビット単位の表現を持つの値があるとします。次に、C 標準では、float -> double -> float 変換が一方から他方に切り替わることを妨げるものは何もありません。IEEE では、パディング ビットがない限り「実際の値」に対しては発生しませんが、IEEE が個別のビット単位の表現を持つ単一の NaN を除外するかどうかはわかりません。いずれにせよ、NaN はそれ自体と同等に比較されるわけではないため、2 つの NaN が「同じ NaN」であるか「異なる NaN」であるかを文字列に変換する以外に判断する標準的な方法はありません。この問題は論外かもしれません。

注意すべきことの 1 つは、コンパイラの非準拠モードです。このモードでは、超正確な値が「隠れて」保持されます。たとえば、中間結果が浮動小数点レジスタに残され、丸められずに再利用されます。サンプルコードが失敗する原因になるとは思いませんが、浮動小数点を実行するとすぐに、==心配し始めるようなものです.

于 2013-02-08T13:20:12.447 に答える
16

C99 から:

6.3.1.5 実数浮動小数点型
1 float が double または long double に拡張された場合、または double が long double に拡張された場合、その値は変更されません。
2 double が float に降格された場合、long double が double または float に降格された場合、またはその意味型 (6.3.1.8 を参照) で必要とされるよりも高い精度と範囲で表されている値が、その意味型に明示的に変換されている場合。変換される値は新しい型で正確に表すことができ、変更されません...

これにより、float->double->float 変換で元の float 値が保持されることが保証されると思います。

標準では、マクロINFINITYNANinも定義されてい7.12 Mathematics <math.h>ます。

4 マクロ INFINITY は、可能な場合、正または符号なし無限大を表す float 型の定数式に展開されます。else は、変換時にオーバーフローする float 型の正の定数に変換されます。
5 マクロ NAN は、実装が float 型の quiet NaN をサポートする場合にのみ定義されます。これは、静かな NaN を表す float 型の定数式に展開されます。

そのため、そのような特別な値が用意されており、変換はそれらに対しても機能する可能性があります (負の無限大と負のゼロを含む)。

于 2013-02-08T13:08:30.860 に答える
2

アサーションは、flush-to-zero および/または denormalized-is-zero モードでは失敗します (例: -mfpmath=sse、-fast-math などでコンパイルされたコードだけでなく、Intel のような既定のコンパイラやアーキテクチャのヒープでも)。 C++ コンパイラ) f が非正規化されている場合。

ただし、そのモードで非正規化されたフロートを生成することはできませんが、シナリオは引き続き可能です。

a) 非正規化フロートは外部ソースから取得されます。

b) 一部のライブラリは、FPU モードを改ざんしますが、関数呼び出しのたびに FPU モードを元に戻すことを忘れる (または意図的に回避する) ため、呼び出し元が正規化を一致させない可能性があります。

以下を出力する実際の例:

f = 5.87747e-39
f2 = 5.87747e-39

f = 5.87747e-39
f2 = 0
error, f != f2!

この例は VC2010 と GCC 4.3 の両方で機能しますが、VC はデフォルトで数学に SSE を使用し、GCC はデフォルトで数学に FPU を使用すると想定しています。それ以外の場合、この例では問題を説明できない場合があります。

#include <limits>
#include <iostream>
#include <cmath>

#ifdef _MSC_VER
#include <xmmintrin.h>
#endif

template <class T>bool normal(T t)
{
    return (t != 0 || fabsf( t ) >= std::numeric_limits<T>::min());
}

void csr_flush_to_zero()
{
#ifdef _MSC_VER
    _MM_SET_FLUSH_ZERO_MODE(_MM_FLUSH_ZERO_ON);
#else
    unsigned csr = __builtin_ia32_stmxcsr();
    csr |= (1 << 15);
    __builtin_ia32_ldmxcsr(csr);
#endif
}

void test_cast(float f) 
{
    std::cout << "f = " << f << "\n";
    double d = double(f);
    float f2 = float(d);
    std::cout << "f2 = " << f2 << "\n";

    if(f != f2)
        std::cout << "error, f != f2!\n";

    std::cout << "\n";
}

int main()
{
    float f = std::numeric_limits<float>::min() / 2.0;

    test_cast(f);
    csr_flush_to_zero();
    test_cast(f);
}
于 2013-02-11T23:12:28.310 に答える