Какой самый большой «относительный» уровень я могу сделать, используя float?


13

Так же, как это было продемонстрировано в таких играх, как dungeon siege и KSP, на достаточно большом уровне начнутся глюки из-за работы плавающей запятой. Вы не можете добавить 1e-20 к 1e20 без потери точности.

Если я решу ограничить размер моего уровня, как рассчитать минимальную скорость, с которой мой объект может двигаться, пока он не начнет изменяться?

Ответы:


26

32-битное число имеет 23-битную мантиссу .

Это означает, что каждое число представлено как 1.xxx xxx xxx xxx xxx xxx xxx xx, умноженное на некоторую степень 2, где каждый x - двоичная цифра, 0 или 1. (За исключением очень маленьких денормализованных чисел, меньших 2-126 - они начинаются с 0. вместо 1., но я буду игнорировать их в дальнейшем)

Таким образом, в диапазоне от 2я и 2(я+1) вы можете представить любое число с точностью ±2(я-24)

Например, для язнак равно0 наименьшее число в этом диапазоне равно (20)1знак равно1 . Следующее наименьшее число - (20)(1+2-23) . Если вы хотите представить 1+2-24 , вам придется округлить в большую или меньшую сторону, чтобы получить ошибку 2-24 любом случае.

In this range:                You get accuracy within:
-----------------------------------------------
         0.25   -     0.5    2^-26 = 1.490 116 119 384 77 E-08
         0.5    -     1      2^-25 = 2.980 232 238 769 53 E-08
         1     -      2      2^-24 = 5.960 464 477 539 06 E-08
         2     -      4      2^-23 = 1.192 092 895 507 81 E-07
         4     -      8      2^-22 = 2.384 185 791 015 62 E-07
         8     -     16      2^-21 = 4.768 371 582 031 25 E-07
        16     -     32      2^-20 = 9.536 743 164 062 5  E-07
        32     -     64      2^-19 = 1.907 348 632 812 5  E-06
        64     -    128      2^-18 = 0.000 003 814 697 265 625
       128    -     256      2^-17 = 0.000 007 629 394 531 25
       256    -     512      2^-16 = 0.000 015 258 789 062 5
       512    -   1 024      2^-15 = 0.000 030 517 578 125
     1 024    -   2 048      2^-14 = 0.000 061 035 156 25
     2 048    -   4 096      2^-13 = 0.000 122 070 312 5
     4 096    -   8 192      2^-12 = 0.000 244 140 625
     8 192   -   16 384      2^-11 = 0.000 488 281 25
    16 384   -   32 768      2^-10 = 0.000 976 562 5
    32 768   -   65 536      2^-9  = 0.001 953 125
    65 536   -  131 072      2^-8  = 0.003 906 25
   131 072   -  262 144      2^-7  = 0.007 812 5
   262 144   -  524 288      2^-6  = 0.015 625
   524 288 -  1 048 576      2^-5  = 0.031 25
 1 048 576 -  2 097 152      2^-4  = 0.062 5
 2 097 152 -  4 194 304      2^-3  = 0.125
 4 194 304 -  8 388 608      2^-2  = 0.25
 8 388 608 - 16 777 216      2^-1  = 0.5
16 777 216 - 33 554 432      2^0   = 1

Поэтому, если ваши единицы измерения - метры, вы потеряете миллиметровую точность в диапазоне 16 484 - 32 768 (примерно 16-33 км от начала координат).

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

  • Если мы используем сантиметры в качестве нашей единицы, мы теряем миллиметровую точность в диапазоне 1 048 576-2 097 152 (10-21 км от начала координат)

  • Если мы используем гектаметры в качестве нашей единицы, мы теряем миллиметровую точность в диапазоне 128-256 (13-26 км от начала координат)

... поэтому изменение единицы измерения на четыре порядка по-прежнему приводит к потере миллиметровой точности где-то в диапазоне десятков километров. Все, что мы сдвигаем, это то, где именно в этой полосе он попадает (из-за несоответствия между нумерацией base-10 и base-2), не сильно расширяя нашу игровую зону.

Точность, с которой допускает ваша игра, будет зависеть от деталей вашего игрового процесса, физического моделирования, размера объекта / расстояния прорисовки, разрешения рендеринга и т. Д., Поэтому сложно установить точное обрезание. Может быть, ваш рендеринг выглядит хорошо в 50 км от начала координат, но ваши пули телепортируются через стены, или чувствительный сценарий геймплея становится бесполезным. Или вы можете найти игру играет хорошо, но все имеет едва ощутимую вибрацию из-за неточностей в преобразовании камеры.

Если вы знаете требуемый уровень точности (скажем, интервал от 0,01 единицы до примерно 1 пикселя на вашем типичном расстоянии просмотра / взаимодействия, а любое меньшее смещение невидимо), вы можете использовать таблицу выше, чтобы найти, где вы теряете это точность и отступить на несколько порядков для обеспечения безопасности в случае операций с потерями.

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


1
Перецентрирование - это определенно путь!
Флорис

2
Как насчет использования координат с фиксированной точкой? Может быть, с 64-битными целыми числами, если это необходимо?
API-Beast

вопрос в том, насколько большим может быть этот перецентрированный регион? если, например, в моей игре я хочу снимать на большом расстоянии с сильным зумом, мне обязательно нужно использовать двойное или достаточно поплавка? не лучше ли перецентрировать в соответствии с четырехугольным деревом или каким-либо алгоритмом мозаики?
Jokoon

Это будет зависеть от числовой стабильности алгоритмов, используемых вашими системами рендеринга и физики - поэтому для данной кодовой базы / движка единственный способ узнать наверняка - это попробовать тестовую сцену. Мы можем использовать таблицу для оценки максимальной точности (например, камера, находящаяся на расстоянии 16 км от объекта, будет иметь тенденцию видеть ошибки размером не менее миллиметра, поэтому ваш зум должен быть достаточно широким, чтобы их размер был меньше пикселя - если зум нужен чтобы быть более жестким для вашей игры, тогда удваивается или может потребоваться какая-то умная математика), но цепочка операций с потерями может столкнуться с проблемами задолго до этого гипотетического предела.
DMGregory

Я до сих пор задаюсь вопросом, что означает перенести мир на графический API. Если у меня есть большой кусок инстансированной (или не инстанцированной) геометрии, все еще безопасно это делать? Я предполагаю, что это означает перевод ВСЕХ преобразований, но если я делаю это несколько раз, разве нет риска потери точности с плавающей запятой?
Jokoon

3

Трудно ответить, так как это зависит от масштаба вашей физики: какова допустимая минимальная скорость движения, которую НЕ нужно округлять до нуля?

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

Например, стрельба из пушечного ядра из любой точки мира даст вам тот же результат, а 64-битная фиксированная точка (32,32) дает вам огромную точность и больше, чем что-либо воспринимаемое в большинстве игр. Если ваше устройство составляет 1 метр, вы по-прежнему на 232 пикометра с точностью 2147483 км от источника.

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

В качестве бонуса широкая фаза и AABB имеют тенденцию быть быстрее в фиксированной точке из-за задержки FPU. Также быстрее преобразовать фиксированную точку в индекс октодерева (или квадродерева), поскольку вы можете выполнять простую битовую маскировку.

Эти операции не получают столько пользы от инструкций SIMD и конвейерной обработки, которые обычно скрывают задержку FPU.

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


-3

Вы можете избежать всего этого путем умножения.
Вместо того, чтобы работать с плавающими числами, просто умножьте их на 10 ^ (x), сохраните их, а при необходимости снова умножьте на 10 ^ (- x).
От этого зависит, какой тип int вы хотите использовать.


2
Это не помогает избежать проблемы. Формат с фиксированной точкой по-прежнему имеет конечную точность и диапазон - чем больше диапазон, тем ниже точность (в зависимости от того, где вы поместили эту фиксированную десятичную точку), поэтому принимается решение: «насколько большим я могу сделать уровень без видимых ошибок округления» все еще применяется.
DMGregory

3
Более того, база-10 не очень практична. Числа с фиксированной запятой работают намного лучше, когда вы разбиваете целое число в терминах битов (рассмотрим 26,6 без знака - дробный компонент - младшие 6 бит ( 1,0 / 64,0 * x & 63), а неотъемлемая часть - просто x >> 6) , Это гораздо проще реализовать, чем возвести что-то в степень десяти.
Андон М. Коулман
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.