Равномерно распределить ячейки сетки по горизонтали / вертикали?
Я пытаюсь нарисовать сетку внутри окна game_width=640
а также game_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 начала горизонтальной линии.
Проблема в том, что в конце она оставляет очень маленькую ячейку:
Я подумал, что это как-то связано с этим интегральным делением, поэтому мы подошли ко второму уравнению:
(int)(i * ((float)game_height / numcells))
Идея состоит в том, чтобы избежать целочисленного деления, деления с плавающей запятой, умножить на i, как раньше, и привести результат обратно к int. Это работает хорошо, нет лишней маленькой ячейки в конце!
Что сводит меня с ума, это уравнение:
i * game_height / numcells
который, кажется, имеет тот же эффект, что и предыдущее уравнение, но, конечно, с дополнительным преимуществом не выполнять кастинг. Я не могу понять, почему это не страдает от проблем целочисленного деления в первом уравнении.
Обратите внимание, что математически: X * (Y / Z) == X * Y / Z Так что определенно есть проблема с целочисленным делением в первом уравнении.
Вот видео с этими 3 уравнениями в окне просмотра отладчика.
Вы можете увидеть увеличивающийся разрыв между результатом первого уравнения по сравнению со вторым и третьим (который всегда имел тот же результат), как i
увеличивается.
Почему третье уравнение дает правильные результаты в качестве второго уравнения и не страдает от интегральной ошибки деления в первом уравнении? Я просто не могу обернуть голову вокруг этого...
Любая помощь приветствуется.
1 ответ
Ответ в порядке выполнения операций. Первое уравнение
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 вместо целочисленного уравнения, чтобы избежать распространенных ошибок, таких как переходные переполнения.