2

unit64 変数と float の乗算を実行しているときに、mingw で奇妙な動作が見られます。これはコンパイラの問題ですか?

以下は私のコードです:

#include <iostream>
using namespace std;

int main() {

    uint64_t myvar = 4293057;
    float mycrasher = 1000;
    myvar = myvar*mycrasher;
    cout << "value after mul is "<<myvar << endl; 
    return 0;
}

表示される出力値は 4293057024 であり、4293057000 ではありません!!

4

1 に答える 1

1

IEEE754 単精度値 ( などfloat) の精度は、10 進数で約 7 桁しかありません。それを超えると、それは不正確です。1000 を掛ける必要さえなく、10 を掛ければ42930568.

次のコードで何が起こっているかを確認できます。

#include <iostream>
int main () {
    float xyzzy = 42930570;
    std::cout.precision (5);
    std::cout << "xyzzy is " << std::fixed <<xyzzy << '\n';
    return 0;
}

それほど正確ではない出力:

42930568.00000

より完全に説明すると、IEEE754 浮動小数点値は、その目的に使用できるビット数が限られているため、精度が制限されています。単精度値の長さは 32 ビットで、そのうちの 23 ビットが小数に使用されます (残りのビットは符号と指数に使用されます)。これらの 23 ビットは、約 7 桁の 10 進数に相当します。詳細な分析はこちらでご覧いただけます

数値 42930570 は、単精度値で正確に表すことができません。であるビットパターンまたは次に高いビットパターン0x4c23c462を取得します。429305680x4c23c46342930572


floatそれらが代わりに変換されている理由uint64_tは、それが標準に記載されているためです。C++03 では、「乗法演算子」セクション (5.6) に次のように記載されています。

オペランドに対して通常の算術変換が実行され、結果の型が決定されます。

通常の算術変換は、セクション 5、パラグラフ 9 で詳述されており、以下で構成されています。

  • いずれかのオペランドが long double 型の場合、もう一方は long double に変換されます。
  • それ以外の場合、いずれかのオペランドが double の場合、もう一方は double に変換されます。
  • それ以外の場合、いずれかのオペランドが float の場合、もう一方は float に変換されます。
  • 整数型を扱うその他の要素 (実際にはそうは言っていませんが、言い換えています)。

afloatと aがあるのでuint64_t、それは上記の 3 番目の箇条書きでカバーされています。「浮動積分変換」セクション (4.9) では、次のように表示されます。

整数型または列挙型の右辺値は、浮動小数点型の右辺値に変換できます。可能であれば、結果は正確です。それ以外の場合は、表現可能な次の低い値または高い値の実装定義の選択です。

したがって、精度の低下が見られるのはそのためです。

それは C++11 でも変わっていません。文言が変更され、もう少し冗長になりますが、セクションは同じ結果になります。

于 2012-12-03T01:00:43.987 に答える