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
を取得します。42930568
0x4c23c463
42930572
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 でも変わっていません。文言が変更され、もう少し冗長になりますが、セクションは同じ結果になります。