Точность вычислений с плавающей точкой и физические расчеты - PullRequest
2 голосов
/ 21 июня 2011

Вектор гравитации 2 в моем физическом мире равен (0; 0,1).

Известно, что число 0,1 проблематично, поскольку «оно не может быть точно представлено, но приблизительно равно 1.10011001100110011001101 × 2-4».

Наличие этого значения для силы тяжести вызывает у меня проблемы со столкновениямии создает довольно неприятные ошибки.Изменение значения на 0,11 решает эти проблемы.

Есть ли более элегантное решение, которое вообще не требует изменения значения?


Видео с ошибкой

http://www.youtube.com/watch?v=bRynch1EtnE


Исходный код

http://pastebin.com/jNkqa3sg

Первый метод (AABBIsOverlapping) проверяет пересечение между двумя объектами AABB.Второй метод (Update) вызывается для каждого тела в каждом кадре.

Я попытаюсь объяснить, как работает метод Update:

  1. Добавить вектор ускорения силы тяжести к вектору скорости
  2. Создайте временный вектор (следующий) и установите для него скорость
  3. Получить тела в пространственном хеше в ячейках вокруг текущего тела
  4. Если есть горизонтальное перекрытие, разрешите его, установите next.X и speed.X на 0 и переместите игрока
  5. Если есть вертикальное перекрытие, разрешите его, установите next.Y и speed.Y на 0 (или на 0,1, чтобы предотвратитьпостоянный прыжок с потолка) и переместите игрока
  6. После окончания цикла, если не было перекрытий, переместите игрока

Ответы [ 4 ]

6 голосов
/ 21 июня 2011

короче нет.Обычное решение - никогда не проверять равенство и всегда проверять диапазон + - эпсилон (очень маленькое значение).

4 голосов
/ 21 июня 2011

В физике неспособность представить число не имеет значения вообще. Единственное, что имеет значение в физике - это накопление ошибок округления.

Я полагаю, что ваша проблема связана с неправильными сравнениями эпсилона или даже без эпсилона. Но без дополнительной информации я не могу вам помочь. Если ваш код не может справиться с небольшими ошибками округления, он имеет недостатки и должен быть исправлен.

Вы можете использовать Decimal для своего математического кода, который может точно представлять 0.1m. Но я не думаю, что это то, что вам действительно нужно, поскольку ваша проблема, скорее всего, не связана с тем фактом, что 0.1 не может быть точно представлено в float.


Одна потенциальная проблема, которую я вижу в вашем коде, заключается в том, что при разрешении столкновений вы перемещаете тело точно к границе столкновения. Возможно, вам нужно продвинуть это эпсилон дальше.

2 голосов
/ 21 июня 2011

Короче говоря - вы не можете работать со значениями с плавающей запятой на ПК, как в реальной жизни.Всегда будут потери точности и ошибки округления из-за ограниченного объема памяти, используемой для хранения значений в очень широких диапазонах.

Всегда проверяйте равенство с некоторыми эпсилонами, которые могут составлять половину шага между рабочими значениями, например 0,1:

IsEqual(0.1, 0.2, 0.05) = false
IsEqual(0.1, 0.1001, 0.05) = true
IsEqual(0.1, 0.1499, 0.05) = true

или лучшая точность в заданном масштабе и заданном формате с плавающей запятой (например, 64-битный формат имеет меньше эпсилона, чем 32-битный) (вам может потребоваться проверить на вашем языке способы получения этого значения):

IsEqual(0.1, 0.2, BestPrecisionAt(0.1)) = false
IsEqual(0.1, 0.1001, BestPrecisionAt(0.1)) = false
IsEqual(0.1, 0.1499, BestPrecisionAt(0.1)) = false
IsEqual(0.1, 0.1000001, BestPrecisionAt(0.1)) = true
//Where for example BestPrecisionAt(0.1) could be 0.00001

РЕДАКТИРОВАТЬ: Вы ничего не сказали об ошибках, которые у вас есть.Так что же не так с 0.1?Я могу только предположить, что ваш временной шаг не является достаточно точным, скорости ваших объектов позволяют им проходить друг через друга между проверками столкновений.Это верно?Если да - вы должны увеличить разрешение временного шага и / или проверить наличие коллизий раньше.

2 голосов
/ 21 июня 2011

Если непредставимость приводит к ошибкам в вашем коде, то ваши алгоритмы в некотором роде ошибочны. Трудно сказать, как они ошибочны, потому что вы не поделились какими-либо подробностями, но использование представимого числа не исправит это.

На самом деле 0.11 также не представимо, и поэтому, если оно решает вашу проблему, оно делает это по какой-то другой причине, кроме этой.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...