Який найбільший "відносний" рівень я можу зробити за допомогою float?


13

Так само, як це було продемонстровано в таких іграх, як осада підземелля та KSP, достатньо великий рівень почне мати глюки через те, як працює плаваюча точка. Ви не можете додати 1e-20 до 1e20, не втрачаючи точності.

Якщо я вирішу обмежувати розмір свого рівня, то як я обчислюю мінімальну швидкість, на яку може рухатися мій об’єкт, поки він не почне забиватися?

Відповіді:


26

32-бітний поплавок має 23-бітну мантісу .

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

Таким чином , в діапазоні від 2i і 2(i+1) , можна уявити будь-яке число в межах точності ±2(i24)

i=0(20)1=1(20)(1+223)1+224224

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. Це також швидше перетворити фіксовану точку в індекс octree (або квадри), так як ви можете зробити просту маскування бітів.

Ці операції не отримують великої користі від інструкцій SIMD та конвеєрного транспорту, які, як правило, приховують затримку FPU.

Ви можете конвертувати позиції у плаваючу крапку ПІСЛЯ, віднімаючи положення камери у фіксованій точці, щоб відобразити все, уникаючи проблем із плаваючою точкою у великому світі та все-таки використовуючи звичайний рендер з використанням плаваючих точок.


-3

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


2
Це не уникне проблеми. Формат з фіксованою точкою все ще має кінцеву точність та діапазон - чим більший діапазон, тим менша точність (виходячи з того, куди ви поставили цю фіксовану десяткову точку) - тому рішення "наскільки великим я можу зробити рівень без видимих ​​помилок округлення" досі застосовується.
DMGregory

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