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?
Odpowiedzi:
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.5f
jest 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ść.
źródło
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
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.
źródło
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.
źródło
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?
źródło
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”.
źródło
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ą.
źródło