Nie, to nie jest kolejne pytanie „Dlaczego jest (1 / 3.0) * 3! = 1” .
Ostatnio dużo czytałem o zmiennoprzecinkowych; a konkretnie, w jaki sposób te same obliczenia mogą dać różne wyniki w przypadku różnych architektur lub ustawień optymalizacji.
Jest to problem z grami wideo, które przechowują powtórki lub są w sieci peer-to-peer (w przeciwieństwie do serwera-klienta), które polegają na tym, że wszyscy klienci generują dokładnie te same wyniki za każdym razem, gdy uruchamiają program - mała rozbieżność w jednym obliczenia zmiennoprzecinkowe mogą prowadzić do drastycznie odmiennego stanu gry na różnych maszynach (lub nawet na tej samej maszynie! )
Dzieje się tak nawet w przypadku procesorów „zgodnych” z IEEE-754 , głównie dlatego, że niektóre procesory (mianowicie x86) używają podwójnej rozszerzonej precyzji . Oznacza to, że używają 80-bitowych rejestrów do wykonywania wszystkich obliczeń, a następnie skracają je do 64- lub 32-bitowych, co prowadzi do innych wyników zaokrągleń niż maszyny, które używają 64- lub 32-bitów do obliczeń.
Widziałem kilka rozwiązań tego problemu online, ale wszystkie dla C ++, a nie C #:
- Wyłącz tryb podwójnej rozszerzonej precyzji (aby wszystkie
double
obliczenia korzystały z 64-bitowego IEEE-754) przy użyciu_controlfp_s
(Windows),_FPU_SETCW
(Linux?) Lubfpsetprec
(BSD). - Zawsze uruchamiaj ten sam kompilator z tymi samymi ustawieniami optymalizacji i wymagaj, aby wszyscy użytkownicy mieli tę samą architekturę procesora (bez odtwarzania na różnych platformach). Ponieważ moim „kompilatorem” jest w rzeczywistości JIT, który może optymalizować inaczej za każdym razem, gdy program jest uruchamiany , nie sądzę, aby było to możliwe.
- Używaj arytmetyki stałoprzecinkowej, unikaj
float
idouble
całkowicie.decimal
działałby w tym celu, ale byłby znacznie wolniejszy i żadna zSystem.Math
funkcji biblioteki go nie obsługuje.
Czy to w ogóle problem w C #? Co jeśli zamierzam obsługiwać tylko system Windows (nie Mono)?
Jeśli tak, czy istnieje sposób, aby zmusić mój program do działania z normalną podwójną precyzją?
Jeśli nie, czy są jakieś biblioteki, które pomogłyby zachować spójność obliczeń zmiennoprzecinkowych?
strictfp
słowo kluczowe, które wymusza wykonywanie wszystkich obliczeń w określonym rozmiarze ( float
lub double
), a nie w rozszerzonym rozmiarze. Jednak Java nadal ma wiele problemów ze wsparciem dla IEE-754. Bardzo (bardzo, bardzo) niewiele języków programowania dobrze obsługuje IEE-754.