8

VC++ 2013 (32 ビット、最適化なし) で次のコードを実行すると:

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

double mulpow10(double const value, int const pow10)
{
    static double const table[] =
    {
        1E+000, 1E+001, 1E+002, 1E+003, 1E+004, 1E+005, 1E+006, 1E+007,
        1E+008, 1E+009, 1E+010, 1E+011, 1E+012, 1E+013, 1E+014, 1E+015,
        1E+016, 1E+017, 1E+018, 1E+019,
    };
    return pow10 < 0 ? value / table[-pow10] : value * table[+pow10];
}

int main(void)
{
    double d = 9710908999.008999;
    int j_max = std::numeric_limits<double>::max_digits10;
    while (j_max > 0 && (
        static_cast<double>(
            static_cast<unsigned long long>(
                mulpow10(d, j_max))) != mulpow10(d, j_max)))
    {
        --j_max;
    }
    double x = std::floor(d * 1.0E9);
    unsigned long long y1 = x;
    unsigned long long y2 = std::floor(d * 1.0E9);
    std::cout
        << "x == " << x << std::endl
        << "y1 == " << y1 << std::endl
        << "y2 == " << y2 << std::endl;
}

私は得る

x  == 9.7109089990089994e+018
y1 == 9710908999008999424
y2 == 9223372036854775808

デバッガーで。

私は気が狂った。誰かが一体どうやって異なる価値観y1を持っているのか説明してもらえますか?y2


アップデート:

これは/Arch:SSE2or/Arch:AVXではなく/Arch:IA32orの下でのみ発生するよう/Arch:SSEです。

4

4 に答える 4

5

double範囲外の値をに変換していますunsigned long long。これは標準の C++ では許可されておらず、Visual C++ は SSE2 モードでこれを非常にひどく扱っているようです。FPU スタックに数値が残り、最終的にオーバーフローして、FPU を使用する後のコードが非常に興味深い方法で失敗します。

縮小サンプルは

double d = 1E20;
unsigned long long ull[] = { d, d, d, d, d, d, d, d };
if (floor(d) != floor(d)) abort();

ullこれは、8 つ以上の要素がある場合は中止されますが、7 つまでの場合はパスします。

解決策は、値が範囲内にあることがわかっていない限り、浮動小数点値を整数型に変換しないことです。

4.9 浮動整数変換 [conv.fpint]

浮動小数点型の prvalue は、整数型の prvalue に変換できます。変換は切り捨てられます。つまり、小数部分は破棄されます。切り捨てられた値を変換先の型で表すことができない場合、動作は未定義です。[注:宛先タイプが の場合はbool、4.12 を参照してください。--巻末注記

符号なしの型に変換されたときに範囲外の値が折り返されるという規則は、値がすでに何らかの整数型である場合にのみ適用されます。

ただし、どんなに価値があっても、これは意図的なものではないように思われるため、標準でこの動作が許可されていても、これをバグとして報告する価値があるかもしれません。

于 2014-01-31T20:00:06.980 に答える
0

y1 を double としてキャストしてから、long に再度キャストしました。x の値は「床」の値ではなく、床の丸められた値です。

整数と浮動小数点数のキャストにも同じロジックが適用されます。float x = (float)((int) 1.5) は float x = 1.5 に異なる値を与えます

于 2014-01-31T11:35:14.640 に答える