水平/垂直均匀分布网格单元?

时间:2015-08-17 02:00:29

标签: c++ winapi math integer-division

我试图在game_width=640game_height=480的窗口内绘制网格。网格单元的数量是预定义的。我希望水平和垂直均匀地分布细胞。

void GamePaint(HDC dc)
{
    int numcells = 11;
    for(int i = 1; i <= numcells; i++)
    {
        int y = <INSERT EQUATION>;
        MoveToEx(dc, 0, y, NULL);
        LineTo(dc, game_width, y);
    }

    // solving the horizontal equation will in turn solve the vertical one so no need to show the vertical code
}

首先想到的是:

i * (game_height / numcells)

这背后的概念是将总高度除以单元格数以获得偶数单元格大小,然后在循环的每次迭代中乘以i以获得开头的正确y坐标水平线。

问题在于它似乎在最后留下了一个额外的小单元格:

enter image description here

我认为这必须与整数除法有关,所以我们来看第二个等式:

(int)(i * ((float)game_height / numcells))

这个想法是避免整数除法,做一个浮点除法,像之前一样乘以i并将结果强制转换为int。这很好用,最后没有额外的小单元!

enter image description here

让我疯狂的是这个等式:

i * game_height / numcells

似乎与前一个等式具有相同的效果,但当然还有不做任何转换的额外好处。我无法弄清楚为什么这不会受到第一个等式中整数除法问题的影响。

注意数学:X *(Y / Z)== X * Y / Z. 因此,第一个等式中的整数除法肯定存在问题。

Here's a video在调试器监视窗口中观察这3个等式。

你可以看到第一个方程的结果与第二个和第三个结果(总是有相同的结果)之间的差距越来越大。{/ p>

为什么第三个等式给出了正确的结果作为第二个等式而没有遇到第一个等式中的积分除法误差?我似乎无法绕过它......

感谢任何帮助。

1 个答案:

答案 0 :(得分:2)

答案是按照执行的操作顺序。第一个等式

int y = i * (game_height / numcells);

首先执行除法,并通过乘以i来放大舍入误差。向下/向右移动越远,累积的舍入误差就越大。

最后一个等式

int y = i * game_height / numcells;

从左到右进行评估。相对误差变小,因为您要分割较大的数字(i * game_height)。您仍然有舍入错误,但它不会累积。事实上,在最终评估中你没有留出额外的空间,i等于numcells,基本上相互抵消。您将始终在最后一次迭代中看到y == game_height

使用浮点运算仍然更准确:虽然使用整数数学的舍入误差在每行的[0 .. numcells)区间内,但浮点运算将其减少到[0 .. 1)。您将看到使用浮点数学的更均匀分布的线。


注意:在Windows上,您可以使用MulDiv而不是整数公式来防止常见错误,例如瞬态溢出。