Равномерно распределить ячейки сетки по горизонтали / вертикали?

Я пытаюсь нарисовать сетку внутри окна 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 вместо целочисленного уравнения, чтобы избежать распространенных ошибок, таких как переходные переполнения.

Другие вопросы по тегам