1

只是想弄清楚下面的这些案例:

#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那么它才成立,mNumRowMax是 8,那么有什么问题'f'

案例2:

float tmp = mMapHeight/( MAP_CELL_SIZE_MIN * mScrHeight );//tmp = 8.0
mNumRowMax = tmp;

mNumRowMax现在是 8,所以任何人都可以帮助我理解第一个案例的问题是什么时候mNumRowMax是 7

4

3 回答 3

2

发生的事情是

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 * 320where的结果r0.1四舍五入b到位,是全精度(忽略 2 的幂),

   6.66...68
 + 1.99...9a
 -----------
   8.00...02

b+3b ≡ 0 (mod 4)b ≡ 1 (mod 4)

   6.66...60
 + 1.99...98
 -----------
   7.ff...f8

b+2b ≡ 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.1is a double(大概有 53 位精度), 这两个floats 被提升为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) == 10.1 * 320 == 32

如果以 64 位或更高的精度(想想 x87)以扩展精度执行计算,则字面量可能0.1根本没有转换为double精度并直接与扩展精度一起使用,这再次导致乘法0.1 * 320导致正好 32。

如果文字0.1以精度使用但计算以更高的精度执行,则如果中间结果直接从具有更高精度的表示中double截断为 7,它将再次产生 7,如果在转换为.intint

(除此之外:gcc/g++ 4.5.1 在所有情况下都会产生 8,无论优化级别如何,在我的 64 位机器上,我还没有在 32 位机器上尝试过。)

¹我不完全确定,但我认为这违反了标准,它应该首先删除多余的精度。有语言律师吗?

于 2012-05-11T12:56:19.960 回答
0

当浮点数转换为整数时,该值被截断而不是四舍五入,即所有小数都被“截断”。

于 2012-04-18T01:59:38.673 回答
0

看来您遇到了舍入错误。

一个简单的解决方法可能是使用 double 而不是 float。

如果这不是一个选项,那么您可能需要四舍五入到整数。例如,如果您有一个浮点值 f,请执行以下操作int x = (int)(f + 0.5);

于 2012-04-18T02:00:52.357 回答