O precyzji zmiennoprzecinkowej i dlaczego nadal jej używamy

38

Punkt zmiennoprzecinkowy zawsze był kłopotliwy z powodu precyzji na dużych światach.

Ten artykuł wyjaśnia za kulisami i oferuje oczywistą alternatywę - numery punktów stałych. Niektóre fakty są naprawdę imponujące, takie jak:

„Cóż, 64 bity precyzji pozwalają dotrzeć do najdalszej odległości Plutona od Słońca (7,4 miliarda km) z dokładnością poniżej mikrometra”.

Dokładność submikrometrowa jest większa niż jakakolwiek potrzeba fps (dla pozycji, a nawet prędkości), i pozwoliłaby ci budować naprawdę duże światy.

Moje pytanie brzmi: dlaczego nadal używamy zmiennoprzecinkowego, jeśli punkt stały ma takie zalety? Większość interfejsów API renderowania i bibliotek fizyki korzysta z liczb zmiennoprzecinkowych (i ma to swoje wady, więc programiści muszą się obejść).

Czy są o wiele wolniejsze?

Ponadto, jak twoim zdaniem skalowalne silniki planetarne, takie jak outerra czy infinity, radzą sobie z dużą skalą? Czy używają stałego punktu dla pozycji, czy mają jakiś algorytm podziału przestrzeni?

system_is_b0rken
źródło
3
Ostatni „dodatkowy” bit powinien być prawdopodobnie osobnym pytaniem, aby główny nie został odsunięty na bok.
Tetrad
Chcę wymyślić, jak wymusić ustalony punkt ... Moja gra zawiera różnego rodzaju dziwne błędy, ponieważ Lua używa FPU, a DirectX również bawi się FPU ... Widziałem takie rzeczy jak 2 * 9000 = 17995 lub 500 * 10 = 4897 to jest naprawdę głupie. Ale najgorszym błędem jest prawdopodobnie ten, który jest omawiany na forach Ogre3D, gdzie 1 + 5 = 4.
Przyspieszenie
3
Nie uzasadniał komentarza; ale jeśli zdecydujesz się na użycie stałych punktów Q-Floats ( en.wikipedia.org/wiki/Q_(number_format) ) to twój przyjaciel; są absurdalnie szybkie i łatwe do wdrożenia.
Jonathan Dickinson
Podsumowując, czy powinienem trzymać się liczb zmiennoprzecinkowych (jeśli pracuję w Javie lub Pythonie)? - Gastón 26 minut temu
Gastón

Odpowiedzi:

20

Jeśli pozwolisz mi na bezwstydną wtyczkę, dam ci przykład z prawdziwej gry, nad którą pracuję (link do filmu na YouTube).

Gra ma nieskończony, generowany proceduralnie świat na silniku fizyki. Wykorzystuje zmiennoprzecinkowe pojedynczej precyzji. Po kilkuset metrach przestrzeni gry zaczynają pojawiać się problemy z precyzją (i stają się coraz gorsze, im dalej otrzymujesz źródło).

Moje rozwiązanie? Co około 200 metrów cofam cały świat z powrotem o 200 metrów w kierunku źródła (jeśli chcesz znaleźć i wypróbować jeden z prototypów na mojej stronie i wyświetlić nakładkę debugowania [w] orld, możesz to zobaczyć).

Dlaczego nie użyć stałego punktu? Czy podwójna precyzja? Zamiast pojedynczej precyzji? Ponieważ wszystko inne używa zmiennoprzecinkowego pojedynczej precyzji!

Używa go silnik fizyki, XNA go używa, dane ładowane na kartę graficzną są sformatowane jako zmiennoprzecinkowe pojedynczej precyzji. Nawet sam język jest zaprojektowany do pracy z liczbami zmiennoprzecinkowymi - pisanie i (co ważniejsze) czytanie 0.5fjest znacznie łatwiejsze niż 0x80000000L.

To po prostu kwestia tego, co jest łatwiejsze w praktyce. Wyraźnym zwycięzcą jest świadomość problemów z precyzją zmiennoprzecinkową i pisanie dość prostych funkcji „cofnij świat do zera” (lub wdrażanie partycjonowania przestrzeni lub cokolwiek, co pasuje do twojej gry).

I wreszcie kolejny przykład - Orbiter to gra (symulacja, naprawdę), która naprawdę musi dbać o precyzję. Nie tylko w kosmosie, ale także w czasie (przyspieszenie czasowe plus orbitujące ciała - nie chcę, żeby spadały teraz z nieba). Wykorzystuje także liczby zmiennoprzecinkowe i stosuje hack, aby zachować stabilność.

Andrew Russell
źródło
Zawsze zastanawiałem się, czy niektóre aplikacje mogą łatwiej przenieść wszystko inne i zachować „odtwarzacz” u źródła. Ciekawe, że nie jestem jedyny!
dash-tom-bang
Podoba mi się twoje rozumowanie. Chciałbym tylko zaznaczyć, że jeśli chodzi o gry wymagające sieci, wydaje się, że punkt stały ma mniej dziwactw / odchyleń (przewidywania klientów są dokładniejsze).
Jonathan Dickinson
Nie rozumiem dobrze twojego argumentu. A tak przy okazji, dlaczego języki nie zaimplementowały jeszcze stałego punktu? Oczywiście wydaje się, że jest to powód, dla którego warto zwrócić uwagę na użycie liczb zmiennoprzecinkowych, ale to nie wyjaśnia, dlaczego punkt stały nie został wszędzie wdrożony.
Jokoon
@ jokoon Myślałem, że moja odpowiedź jest dość jasna. Dlaczego punkt stały nie jest wszędzie wdrażany? Wystarczy spojrzeć na Wikipedię: „Bardzo niewiele języków komputerowych ma wbudowaną obsługę stałych wartości punktowych, ponieważ w większości aplikacji binarne lub dziesiętne reprezentacje zmiennoprzecinkowe są zwykle prostsze w użyciu i wystarczająco dokładne ”. Dobry projektant wie, co pominąć - a dobrym kandydatem jest coś, co powiela funkcjonalność w sposób, który naprawdę ułatwia programistom strzelanie sobie w stopę.
Andrew Russell
Zgoda. Dzisiejsze procesory wykonują instrukcje zmiennoprzecinkowe bardzo łatwo, instrukcje mają doskonałą przepustowość, a większość z nich ma tylko kilka cykli dłuższe opóźnienie niż instrukcje liczb całkowitych.
doug65536
11

Po pierwsze - tak, są znacznie szybsze. Nawet jeśli możesz uzyskać stały punkt działający tak szybko jak „normalny” FPU, prawdziwy zmiennoprzecinkowy ma korzystne instrukcje, takie jak fsel, aby zatrzymać rozgałęzienie, lub SIMD, aby pracować na wielu pływakach jednocześnie. Procesory graficzne również używają liczb zmiennoprzecinkowych, przynajmniej w interfejsach użytkownika.

Po drugie, 64 bity prowadzą również daleko w zmiennoprzecinkowe - większość ludzi nadal używa 32, ale podstawową zaletą jest to, że skaluje się. Ta skala punktów stałych ma stałą dokładność. Niezależnie od tego, czy mierzysz Słońce do Plutona, czy po drugiej stronie ulicy, masz taką samą precyzję. Zmienny punkt daje znacznie dokładniejsze wyniki, gdy wszystkie zaangażowane wartości są mniejsze. Ponieważ oczekuje się, że biblioteki fizyki ogólnej będą działały co najmniej biernie z wieloma grami w różnych skalach - a niektóre gry same mogą mieć bardzo różne skale - muszą używać liczby, która działa w wielu skalach.


źródło
5

Inną ważną kwestią do zrobienia jest to, że zmiennoprzecinkowe nie są tak niedokładne, jak ludzie tutaj myślą. 32-bitowa liczba zmiennoprzecinkowa ma 24-bitową dokładność całkowitą. Oznacza to, że jest on co najmniej tak dokładny jak 24-bitowa stała wartość punktu dla dowolnego danego zakresu. Podczas gdy liczby zmiennoprzecinkowe stają się mniej dokładne, im większa staje się ta wartość, stała wartość punktu po prostu przepełni się i zawinie w pewnym momencie. Zmniejszenie dokładności jest lepszą rezerwą. Pływaki mogą się również przepełniać, ale znacznie, znacznie później. Chciałbym zobaczyć wasze twarze, gdy wasz świat nagle zawija się do -2 ^ 31 z powodu stałego przelania punktu.

64-bitowe wartości zmiennoprzecinkowe mają 53-bitową precyzję liczb całkowitych, więc są naprawdę dokładne.

rasmus
źródło
Wartości zmiennoprzecinkowe tak naprawdę nie przepełniają się; jeśli wynik obliczeń jest zbyt duży, aby można go było przedstawić, wynik jest dodatnią lub ujemną nieskończonością.
Ananas
3

W kontekście FPS wartości stałoprzecinkowe mogą w rzeczywistości stanowić zobowiązanie. Dokładniejsza jest liczba zmiennoprzecinkowa bliska zeru. To jest tylko na dużych odległościach stały punkt staje się bardziej preferowany. Odpowiedź brzmi po prostu, że zależy to od kontekstu.

W czymś takim jak galaktyka możesz używać ram odniesienia. Użyj ogromnej skali dla układów słonecznych, a następnie użyj środka Słońca (lub podobnego punktu) jako punktu początkowego dla wszystkiego wewnątrz układu. Korzystając z tego systemu, możesz mieć swoje ciasto i zjeść je, że tak powiem, i nie jest trudne do wyobrażenia.

IIRC, twórca Infinity stwierdził, że w jednym ze swoich wywiadów nieustannie iteruje kwestie skali.

Rushyo
źródło
Jest mało prawdopodobne, aby w każdej grze potrzebna była precyzja zmiennoprzecinkowa „bliska zeru”. Jeśli masz jednostkę jednego metra, 10 bitów precyzji zapewnia dokładność poniżej milimetra i nadal pozwala modelować ponad 4000 km w każdym kierunku, jeśli utkniesz z wartościami 32-bitowymi. Jeśli potrzebujesz większej precyzji niż milimetr, możesz z pewnością przesunąć kilka dodatkowych bitów. Przejście do 64-bitowych wartości w grze kosmicznej dałoby ci układ słoneczny (lub więcej?) Ze stałą precyzją w całym wolumenie.
dash-tom-bang
W rzeczy samej. I dlatego użyłbyś Słońca w Układzie Słonecznym jako punktu odniesienia w świecie wielkości galaktyki! Chodzi o to, że wybrana metoda obsługi precyzji nie wnosi niczego do tabeli. Jest to dyskusja, więc równie dobrze możesz użyć tej, do której dostosowana jest twoja biblioteka / sprzęt.
Rushyo
Nawiasem mówiąc, pracowałem nad grą, w której musieliśmy modelować ponad 4000 km w każdym kierunku. Aby zachować zgodność z kodem w innych projektach, musiał użyć 32 bitów dla pozycji. Nie jest to problem teoretyczny, biorąc pod uwagę komercyjne wymagania dotyczące ponownego użycia kodu i rozmiar dzisiejszych światów gier.
1

Jeśli jeszcze tego nie zrobiłeś, zdecydowanie zapoznaj się z samouczkiem Planet Rendering na GameDev.net. Jeśli chodzi o podział przestrzeni, jednym rozwiązaniem jest utrzymanie dwóch oddzielnych zmiennych pozycji - jednej skali makro i jednej skali mikro. Działa to całkiem dobrze (przetestowane).

Dokładne rozwiązanie zależy od tego, jak zamierzasz radzić sobie z ekstremalnymi odległościami w silniku - czy planujesz przeskoki czy kompresję czasu?

Kornel Kisielewicz
źródło
1

Jednym z powodów jest to, że arytmetyka zmiennoprzecinkowa jest „wystarczająco dobra” (a przynajmniej była), szybko daje dość dokładne wyniki.

Tak długo, jak zdajesz sobie sprawę z ograniczeń arytmetyki zmiennoprzecinkowej i zmieniasz swoje algorytmy, aby sobie z nimi poradzić (patrz odpowiedź Andrew Russella), będziesz tworzyć kod, który „działa”.

ChrisF
źródło
-1

Piszę grę. W mojej grze / programie rysuję statek kosmiczny u źródła za pomocą dość nieruchomej kamery, a moją planetę rysuję za pomocą osobnej kamery. Te dwie rzeczy zajmują się tym problemem, ale moja planeta nie jest jeszcze zbyt szczegółowa. Jeśli chodzi o rozwiązanie, o którym wspominał Andrew Russell o przeniesieniu świata (zakładam, że kamera i jej mieszkańcy wracają do źródła), naprawdę nie spróbowałbym tego, jeśli kiedykolwiek planujesz uczynić grę grą sieciową. Gdyby przenieść świat w aplikacji serwera na podstawie ruchów każdego klienta, świat musiałby walczyć o pozycję. I cały czas zajmowałby swoją pozycję od wszystkich graczy, co byłoby ogromną usterką.

użytkownik1727819
źródło