发生的事情是
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 的二进制浮点表示。
在分数十六进制表示法中,
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)
其中小数部分的最后一个十六进制数字分别在 3、4、1、2 个最高有效位之后被截断。
现在320 = 2^6*(2^2 + 1)
,所以r * 320
where的结果r
被0.1
四舍五入b
到位,是全精度(忽略 2 的幂),
6.66...68
+ 1.99...9a
-----------
8.00...02
与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)
。
在每种情况下,将结果四舍五入到b
精确位会产生 32,然后您将得到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) - ...)
在情况 1 中,结果直接int
从中间结果转换为¹。由于中间结果略小于 8,因此它被截断为 7。
在情况 2 中,中间结果float
在转换为 之前存储在 a 中int
,因此它首先被舍入到 24 位精度,结果正好是 8。
现在如果你去掉f
后缀, 0.1
is a double
(大概有 53 位精度), 这两个float
s 被提升为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,它将再次产生 7,如果在转换为.int
int
(除此之外:gcc/g++ 4.5.1 在所有情况下都会产生 8,无论优化级别如何,在我的 64 位机器上,我还没有在 32 位机器上尝试过。)
¹我不完全确定,但我认为这违反了标准,它应该首先删除多余的精度。有语言律师吗?