Чому роздільна здатність чисел з плаваючою комою зменшується далі від початку?


19

У моїй сцені OpenGL є об'єкти, розташовані на смішно далекій відстані від походження. Коли я переглядаю ці об’єкти, а також переміщую / обертаю / збільшую камеру навколо них, вони 'тремтять'. Тобто вершини, що складаються з об’єктів, здаються, що обертаються навколо уявної 3d сітки точок. Я читав, що це поширена проблема через кількість інформації, яку можна зберігати за допомогою точності з плаваючою комою (яку OpenGL використовує, і майже все, що інше використовує). Я не розумію, чому це відбувається.

Шукаючи рішення, я натрапив на дуже просте виправлення "плаваючого походження", і, здається, це працює. Я просто перетворюю все, щоб мої об'єкти були в однакових відносних положеннях, але те, що дивиться моя камера, близьке до походження. Тут я знайшов пояснення: http://floatingorigin.com/ , але не зміг дотримуватися цього.

Отже ... Чи могла б хтось пояснити, чому розташування моєї сцени дуже далеко (скажімо, 10 мільйонів одиниць) від походження призводить до хаотичної поведінки, яку я спостерігав? А також чому переміщення його близько до джерела виправляє проблему?


4
Тому що якщо вони цього не зробили, вони були б фіксованими точками. Тавтологічне питання, це.
MSalters

1
Правда, але лише тоді, коли ти зрозумієш, що справді означає "плаваюча точка".
Килотан

Відповіді:


26

Це ВСЕ через те, як плаваючі точки представлені на комп'ютерах.

Цілі особи зберігаються досить просто; кожна одиниця є рівно «одиницею», крім «попередньої» так само, як ви очікували при підрахунку числа.

З числами з плаваючою комою це не зовсім так. Натомість декілька бітів вказують на ВІДКРИТИЙ, а решта вказують на те, що відоме як мантіса , або дробова частина, яка потім МНОГОПОЛОЖЕНА складовою частиною (неявно 2 ^ exp), щоб дати остаточний результат.

Подивіться тут для візуального пояснення бітів.

Саме завдяки тому, що цей показник є фактичною частиною бітів, точність починає ВІДБУВАТИ, коли числа зростають.

Щоб побачити це в дії, давайте зробимо зображення з плаваючою плаваючою точкою, не потрапляючи в азотно-зернисту форму: візьміть невеликий показник на зразок 2 і зробимо кілька дробових частин для тестування:

2 * 2 ^ 2 = 8

3 * 2 ^ 2 = 12

4 * 2 ^ 2 = 16

... тощо.

Ці цифри не розростаються дуже далеко лише за показником 2. Але тепер спробуємо показник 38:

2 * 2 ^ 38 = 549755813888

3 * 2 ^ 38 = 824633720832

4 * 2 ^ 38 = 1099511627776

Ого, величезна різниця зараз!

Приклад, хоча конкретно не переходить до ДУЖЕ СЛІДНОГО КОЛІКУВАННЯ (це була б наступна дробова частина залежно від кількості бітів), чи є для того, щоб продемонструвати втрати точності, коли числа зростатимуть. "Наступний лічильний" блок в плавках дуже маленький з малими експонентами і ДУЖЕ великий з більшими показниками, тоді як у цілих числах ВЖЕ 1.

Причина методу поплавкового походження працює в тому, що він масштабує всі ці потенційно великі експоненти з плаваючою точкою ДО ВЕЛИКОГО малого експонента, щоб "наступні лічильники" (точність) могли бути дуже маленькими і щасливими.


Наведені вами приклади були справді наочні, дякую :)
Пр.,

3
На правильному шляху, але я б хотів, щоб ви використовували приклади, ближчі до того, як дійсно працює плаваюча точка. Це не піднімає мантісу до показника; це mantissa * 2 ^ показник.
Натан Рід

3
Ти маєш рацію, я це знав; Я не знаю, що я думав. Відредагував мою відповідь.

1
@ScottW Приємна редакція! +1
Натан Рід

17

Оскільки числа з плаваючою комою представлені у вигляді дробу + показник + знак, і у вас є лише фіксована кількість бітів для частини дробу.

http://en.wikipedia.org/wiki/Single_precision

Коли ви отримуєте все більші та більші числа, у вас просто немає бітів, щоб представляти менші частини.


8

Класика в цій галузі повинна виховуватися: Що повинен знати кожен інформатик про числа з плаваючою комою .

Але суть цього стосується того, як одиничні (подвійні) точні числа з плаваючою точкою - це лише 32-бітове (64-бітове) двійкове число з 1 бітом, що представляє знак, 8-бітний (11-бітний) показник бази 2 , і 23-бітове (52-бітове) значенняі в дужках є значення для парних).

Це означає, що найменше додатне число, яке ви можете представити в одній точності, становить 0,0000000000000000000001 x 2 -127 = 2 -22 x 2 -127 = 2 -149 ~ 1,40 x 10 -45 .

Наступне додатне число подвійне, що: 0,0000000000000000000010 х 2 -127 = 2 -148 ~ 2,80 х 10 -45 , а потім наступне число - це сума попередніх двох 0,0000000000000000000011 х 2 -127 = 3 х 2 -149 ~ 4,2 - 45 .

Це продовжує збільшуватися на ту ж постійну різницю, поки: 0,1111111111111111111111 х 2 -127 = 2 -126 - 2 149 ~ 1.17549435 х 10 -38 - 0,00000014 х 10 -38 = 1,17549421 х 10 -38

Тепер ви досягли нормальних чисел (де перша цифра у значенніі 1), зокрема: 1.0000000000000000000000 x 2 -126 = 2 -126 = 1.17549435 x 10 -38, а наступне число - 1.0000000000000000000001 x 2 -126 = 2 -126 (1 + 2 -22 ) = 1.17549435 х 1.00000023.


2

Причина, чому числа з плаваючою комою стають менш точними від початку, полягає в тому, що число з плаваючою комою повинно бути здатним представляти великі числа. Те, як це робиться, надає йому термін "плаваюча точка". Він розбиває можливі значення, які він може приймати (що визначається його бітовою довжиною), щоб було приблизно однакове число для кожного експонента: Для 32-бітного поплавця 23 з бітів визначають мантісу або значення. Таким чином, він зможе прийняти значення 2 ^ 23 різних значень у кожному діапазоні експонентів. Один з таких діапазонів експонентів - 1-2 [2 ^ 0 до 2 ^ 1], тому розділення діапазону від 1 до 2 на 2 ^ 23 різних значень дозволяє досягти великої точності.

Але розділення діапазону [2 ^ 10 до 2 ^ 11] на 2 ^ 23 різних значень означає, що простір між кожним значенням набагато більший. Якби цього не було, то 23 біта було б недостатньо. Вся справа в компромісі: вам потрібно нескінченна кількість біт, щоб представляти будь-яке дійсне число. Якщо ваша програма працює таким чином, що дозволяє вам піти з меншою точністю для більших значень, і ви маєте перевагу від того, щоб насправді представляти великі значення , ви використовуєте подання з плаваючою комою.


просто роблю зауваження тут при подальшому огляді через 7 років ... мої номери в моїх прикладах не особливо підібрані. Але загальні бали дійсні.
Стівен Лу

1

Надавати конкретні приклади того, як працює точність з плаваючою комою, може бути досить складно. Щоб доповнити інші відповіді, ось одна. Скажімо, у нас є десяткове число з плаваючою комою з трьома цифрами мантіси та однією цифрою показника:

мантіса × 10 показник

Коли показник дорівнює 0, кожне ціле число в діапазоні 0–999 може бути представлено точно. Коли це 1, ви по суті множите кожен елемент цього діапазону на 10, тому ви отримуєте діапазон 0–9990; але зараз можна представити лише кратні 10, тому що у вас є лише три цифри точності. Коли показник дорівнює максимуму 9, різниця між кожною парою представлених цілих чисел становить один мільярд . Ви буквально торгуєте точністю для дальності.

Це працює так само, як і з двійковими числами з плаваючою комою: щоразу, коли експонент піднімається на одиницю, діапазон подвоюється , але кількість репрезентативних значень у цьому діапазоні зменшується вдвічі . Це стосується і дробових чисел, що, звичайно, є джерелом вашої проблеми.


0

Взагалі роздільна здатність погіршується, оскільки роздільна здатність множиться на значення показника (2 ** частина експоненту).

на знак підтвердження коментаря Джоша: вищевикладене полягало лише в тому, щоб відповісти на короткий виклад. Звичайно, як я намагався вказати на http://floatingorigin.com/ , це тільки починається до загального рішення, і ваша програма може мати тремтіння з багатьох місць: в точному трубопроводі або інших частинах коду .


Це не додає нічого, чого немає в інших відповідях.

Правда: я зрозумів, що можу описати відповідь в одному рядку, і подумав, що хтось може знайти кращу відповідь корисною.
Кріс Торн

-1

Буфер глибини OpenGL не є лінійним . Чим далі ви йдете, тим гірша роздільна здатність. Рекомендую прочитати це . Щось узяте звідти (12.070):

Підводячи підсумок, розрізнення в перспективі за своєю природою спричиняє більш високу точність Z ближче до передньої частини об'єму огляду, ніж біля задньої.

І ще один (12.040):

Можливо, ви налаштували свої відсічні площини zNear та zFar таким чином, що суттєво обмежує точність буферної глибини. Як правило, це викликано значенням площини відсікання zNear, яке занадто близько до 0,0. Оскільки площина відсікання zNear встановлюється все ближче до 0,0, ефективна точність буфера глибини різко зменшується. Переміщення площини відсікання zFar далі від очей завжди негативно впливає на точність буфера глибини, але це не настільки драматично, як переміщення площини відсічення zNear.

Таким чином, ви повинні перемістити найближчу площину відсікання якнайдалі, а далеку площину - найближчу.


-1: питання стосується точності з плаваючою комою, а не питань точності з нелінійним поданням буфера глибини.
Натан Рід

Можливо, те, що я бачу, пов’язане з проблемами глибинного буферизації. Я використовую лінз поверх OpenGL для перегляду моєї сцени, і я припускаю, що його налаштування камери, перегляду, площин відсікання поблизу та вдалині враховують розміри та положення геометрії (оскільки глядач інструмент, здається, автоматично встановлює оптимальний перегляд вмісту сцени). Але я думаю, що це може бути не так - я спробую пограти з відсікаючими площинами, що залишають недоторканою вихідну позицію, і побачу, що відбувається.
Пр.

2Натан Рід: Автор писав, що у нього є сцена OpenGL, тому я подумав, що це може бути і ця проблема.
zacharmarz

Ця проблема може здатися подібною або пов’язаною, але значення буфера глибини, безумовно, НЕ зберігаються таким чином, що сумісний з числами з плаваючою комою. Це формат з фіксованою точкою. Саме через це відповідь може бути оманливою.
Стівен Лу
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.