只想清楚以下这些案例:
#define MAP_CELL_SIZE_MIN 0.1f
float mMapHeight = 256;
float mScrHeight = 320;
int mNumRowMax;
案例1:
mNumRowMax = mMapHeight/( MAP_CELL_SIZE_MIN * mScrHeight );
mNumRowMax
现在是7,但实际上它必须是8(256/32),如果我将MAP_CELL_SIZE_MIN
的定义更改为仅0.1
,那么它将成立,{{ 1}}是8,所以mNumRowMax
案例2:
'f'
float tmp = mMapHeight/( MAP_CELL_SIZE_MIN * mScrHeight );//tmp = 8.0
mNumRowMax = tmp;
现在是8,所以当mNumRowMax
为7时,任何人都可以帮助我理解第一种情况的错误
答案 0 :(得分:2)
会发生什么
10浮动操作数和浮动表达式结果的值可以比该类型所要求的更精确和范围表示;因此,类型不会改变。 55)
55)强制转换和赋值运算符仍必须执行5.4,5.2.9和5.17中描述的特定转换。
(C ++ 03; C99中几乎相同的6.3.1.8(2)和C11的n1570草案;我确信G ++在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
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)
的{{1}}位。
在每种情况下,将结果四舍五入到b ≡ 3 (mod 4)
位的精度恰好为32,然后得到b
作为最终结果。但如果使用具有更高精度的中间结果,则计算结果为
256/32 = 8
略小于或大于8.
典型的32位256/(0.1 * 320)
具有24(23 + 1)位精度,如果中间结果以至少53位的精度表示:
float
在案例1中,结果直接从中间结果转换为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) - ...)
。由于中间结果略小于8,因此会被截断为7。
在第2种情况下,中间结果在转换为int
之前存储在float
中,因此首先将其舍入为24位精度,结果恰好为8。
现在,如果您不使用int
后缀,f
是0.1
(大概有53位精度),则两个double
会被提升为{{ 1}}用于计算,
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
执行的。
如果以64位精度(想想x87)或更高精度以扩展精度执行计算,则文字1 + 2^(-55) == 1
可能根本不会转换为0.1 * 320 == 32
精度并直接使用使用扩展精度,再次导致乘法0.1
,结果恰好为32。
如果以double
精度使用文字0.1 * 320
,但计算以更高的精度执行,如果中间结果从表示中直接截断为0.1
,则会再次产生7精度更高,如果在转换为double
之前删除了多余的精度,则为8。
(旁白:gcc / g ++ 4.5.1在所有情况下都会产生8,无论优化级别如何,在我的64位盒子上,我都没有尝试过32位盒子。)
¹我不完全确定,但我认为这违反了标准,它应该首先删除多余的精度。任何语言律师?
答案 1 :(得分:0)
当浮点数转换为整数时,该值为截断而不是舍入,即所有小数都只是“切断”。
答案 2 :(得分:0)
您似乎遇到了舍入错误。
一个简单的修复可能是使用double而不是float。
如果这不是一个选项,那么您可能需要舍入到整数。例如,如果浮点值为f,则执行相当于int x = (int)(f + 0.5);