Float変数が奇妙な方法でポイントの後に数字を削減することにより値を節約するのはなぜですか?
-
27-10-2019 - |
質問
私はこのシンプルなコードラインを持っています:
float val = 123456.123456;
このvalを印刷したり、スコープで見たりすると、値123456.13を保存します
わかりました、それは大丈夫です、それは4バイトでポイントの後にそれらのすべての数字を保存することはできませんが、なぜそれはポイントの後に13を作るのですか? 12であるべきではありませんか?
(Win32でVC ++ 2010 Expressを使用)
解決
フロートとして表される場合、あなたの数字の指数は16です(つまり、値はマンティスタイム2^16または65536です)。その後、マンティスはなります
123456.123456 / 65536 = 1.8837909462890625
32ビットのフロートに収まるために、マンティスは23ビットに切り捨てられるので、今はなります 1.883791
. 。後ろに掛けられたとき 65536
, 、 あれは。。。になる 123456.125
.
注意してください 5
小数点の後の3番目の位置:使用したC ++の出力ルーチンはそれを丸め、最終番号を次のようにします 123456.13
.
編集 丸めの説明:(リック・リーガンのコメント)
丸めは、最初にバイナリ(24ビットまで)、小数からバイナリ変換、次に10進数で発生します。 printf
. 。保存された値は1.1110001001000000001 x 2^16 = 1.8837909698486328125 x 2^16 = 123456.125です。それは123456.13として印刷されますが、それはVisual C ++が「ゼロからのラウンドハーフ」を使用しているからです。
リックには 主題に関する傑出した記事, 、 それも。
他の数字とそのフロートの表現で遊んでみたい場合は、ここにあります 非常に便利なIEEE-754計算機.
他のヒント
バイナリでは、123456.123456は11110001001000000.00011111001 ...(無限)です。 11110001001000000.001または123456.125に丸められます。 それか 印刷すると、123456.13に丸めます。
で保存された値 val
に等しい 123456.125
. 。あなたは得ています .13
あなたがそれを丸くしているので:
float val = 123456.123456;
printf("%.4f %.2f\n", val, val);
出力: 123456.1250 123456.13
この場合は、切り捨てを避けるためにダブルを使用する必要があります。コンパイラもあなたに警告する必要があります: 「警告C4305:「初期化」:「ダブル」から「フロート」への切り捨て」.
の値を印刷してみてください std::numeric_limits<float>::digits10
. 。これは大まかに言えば、フロートが持っているベース10の精度です。あなたはそれを超えようとしているので、あなたは精度の損失を経験しています(つまり、重要なものを超えた数字は本当に意味がないことを意味します)。
完全にコンパイラに依存しています。 GCCで確認してください。 xxx.12である必要があります