何が起こるかは
5 [式]
10 浮動オペランドの値および浮動式の結果の値は、型で必要とされるよりも高い精度と範囲で表すことができます。それによって型が変更されることはありません。55)
55)キャストおよび代入演算子は、5.4、5.2.9、および 5.17 で説明されているように、特定の変換を引き続き実行する必要があります。
(C++03; C99 の 6.3.1.8(2) と C11 の n1570 ドラフトと実質的に同一; C++11 の要点は同一であると確信しています。)
以下では、IEEE-754 のような 2 進浮動小数点表現を想定しています。
分数の 16 進数表記では、
1/10 = 1/2 * 3/15
= 1/2 * 0.33333333333...
= 2^(-4) * 1.999999999...
b
したがって、それが精度のビットに丸められると、
2^(-4) * 1.99...9a // if b ≡ 0 (mod 4) or b ≡ 1 (mod 4)
2^(-4) * 1.99...98 // if b ≡ 2 (mod 4) or b ≡ 3 (mod 4)
ここで、小数部分の最後の 16 進数は、それぞれ 3、4、1、2 の最上位ビットの後に切り捨てられます。
ここで、ビットに丸められたwhere320 = 2^6*(2^2 + 1)
の結果は、完全な精度で (2 の累乗を無視して)、r * 320
r
0.1
b
6.66...68
+ 1.99...9a
-----------
8.00...02
orおよびb+3
のビット付きb ≡ 0 (mod 4)
b ≡ 1 (mod 4)
6.66...60
+ 1.99...98
-----------
7.ff...f8
またはb+2
のビット付き。b ≡ 2 (mod 4)
b ≡ 3 (mod 4)
いずれの場合も、結果を精度のビットに丸めると正確に 32 になり、最終結果としてb
得られます。256/32 = 8
しかし、精度の高い中間結果を使用すると、
256/(0.1 * 320)
8より少し小さいか大きい。
24 (23+1) ビットの精度を持つ典型的な 32 ビットfloat
では、中間結果が少なくとも 53 ビットの精度で表される場合:
0.1f = 1.99999ap-4
0.1f * 320 = 32*(1 + 2^(-26))
256/(0.1f * 320) = 8/(1 + 2^(-26)) = 8 * (1 - 2^(-26) + 2^(-52) - ...)
int
ケース1では、結果は中間結果から直接変換されます¹。中間結果は 8 よりわずかに小さいため、7 に切り捨てられます。
float
ケース 2 では、中間結果はに変換される前に a に格納されるint
ため、最初に 24 ビットの精度に丸められ、正確に 8 になります。
ここで、f
サフィックスを省略した場合0.1
はdouble
(おそらく 53 ビットの精度で)、2 つの s が計算のためにfloat
昇格され、double
0.1 = 1.999999999999ap-4
0.1 * 320 = 32*(1 + 2^(-55))
256/(0.1 * 320) = 8 * (1 - 2^(-55) + 2^(-110) - ...)
計算がdouble
精度1 + 2^(-55) == 1
で実行され、すでに0.1 * 320 == 32
.
計算が 64 ビットの精度 (x87 と考えてください) 以上の拡張精度で実行される場合、リテラル0.1
は精度にまったく変換されずdouble
、拡張精度で直接使用される可能性が高く、これも乗算の0.1 * 320
結果となります。ちょうど32。
リテラル0.1
が精度で使用されdouble
、計算がより高い精度で実行される場合、中間結果がより高い精度の表現から に直接切り捨てられた場合は 7 になりint
、余分な精度が に変換される前に削除された場合は 8 になりint
ます。
(余談: gcc/g++ 4.5.1 は、最適化レベルに関係なく、すべてのケースで 8 を生成します。私の 64 ビット ボックスでは、32 ビット ボックスでは試していません。)
¹完全にはわかりませんが、それは標準に違反していると思います。最初に余分な精度を削除する必要があります。言語弁護士はいますか?