Udostępnij za pośrednictwem


Problemy przy migracji liczb zmiennoprzecinkowych

Czasami podczas uaktualniania projektów do nowszej wersji programu Visual Studio może się okazać, że wyniki niektórych operacji zmiennoprzecinkowych uległy zmianie. Zazwyczaj dzieje się tak z jednego z dwóch powodów: zmiany generowania kodu, które lepiej korzystają z dostępnego procesora, oraz poprawki błędów lub zmiany algorytmów używanych w funkcjach matematycznych w bibliotece środowiska uruchomieniowego języka C (CRT). Ogólnie rzecz biorąc, nowe wyniki są poprawne w granicach określonych przez standard języka. Przeczytaj, aby dowiedzieć się, co się zmieniło, i jeśli jest to ważne, jak uzyskać te same wyniki, które funkcje dostały wcześniej.

Nowe funkcje matematyczne i uniwersalne zmiany CRT

Większość funkcji matematycznych CRT jest dostępna w programie Visual Studio od lat, ale począwszy od programu Visual Studio 2013 wszystkie funkcje wymagane przez iso C99 są uwzględniane. Te funkcje są implementowane w celu zrównoważenia wydajności z poprawnością. Ponieważ generowanie poprawnie zaokrąglonych wyników w każdym przypadku może być zbyt drogie, te funkcje są zaprojektowane tak, aby efektywnie uzyskać bliskie przybliżenie do poprawnie zaokrąglonego wyniku. W większości przypadków wygenerowany wynik znajduje się w jednostce +/-1 o najmniejszej precyzji lub ulp prawidłowego zaokrąglonego wyniku, chociaż mogą wystąpić przypadki, w których występuje większa niedokładność. Jeśli używasz innej biblioteki matematycznej, aby uzyskać te funkcje wcześniej, różnice implementacji mogą być odpowiedzialne za zmianę wyników.

Gdy funkcje matematyczne zostały przeniesione do uniwersalnego CRT w programie Visual Studio 2015, niektóre nowe algorytmy zostały użyte, a kilka usterek w implementacji funkcji, które były nowe w programie Visual Studio 2013, zostały naprawione. Te zmiany mogą prowadzić do wykrywalnych różnic w wynikach obliczeń zmiennoprzecinkowych korzystających z tych funkcji. Funkcje, które miały problemy z usterkami, to erf, exp2, remainder, remquo, scalbln i scalbn oraz ich zmiennoprzecinkowe i długie podwójne warianty. Inne zmiany w programie Visual Studio 2015 rozwiązały problemy z zachowaniem informacji o stanie zmiennoprzecinkowych i informacji o stanie wyjątku w _clear87, _clearfp, fegetenv, fesetenv i feholdexcept.

Różnice procesora i flagi kompilatora

Wiele funkcji biblioteki matematycznej zmiennoprzecinkowych ma różne implementacje dla różnych architektur procesora CPU. Na przykład 32-bitowy X86 CRT może mieć inną implementację niż 64-bitowa X64 CRT. Ponadto niektóre funkcje mogą mieć wiele implementacji dla danej architektury procesora CPU. Najbardziej wydajna implementacja jest wybierana dynamicznie w czasie wykonywania w zależności od zestawów instrukcji obsługiwanych przez procesor. Na przykład w 32-bitowej architekturze X86 CRT niektóre funkcje mają implementację x87 i implementację SSE2. W przypadku uruchamiania na procesorze, który obsługuje SSE2, używana jest szybsza implementacja SSE2. W przypadku uruchamiania na procesorze CPU, który nie obsługuje protokołu SSE2, używana jest wolniejsza implementacja x87. Może to być widoczne podczas migrowania starego kodu, ponieważ domyślna opcja architektury kompilatora x86 została zmieniona na /arch:SSE2 w programie Visual Studio 2012. Ponieważ różne implementacje funkcji biblioteki matematycznej mogą używać różnych instrukcji procesora CPU i różnych algorytmów do generowania wyników, funkcje mogą generować różne wyniki na różnych platformach. W większości przypadków wyniki znajdują się w ciągu +/-1 ulp poprawnie zaokrąglonego wyniku, ale rzeczywiste wyniki mogą się różnić w zależności od procesorów CPU.

Ulepszenia poprawności generowania kodu w różnych trybach zmiennoprzecinkowych w programie Visual Studio mogą również mieć wpływ na wyniki operacji zmiennoprzecinkowych, gdy stary kod jest porównywany z nowym kodem, nawet w przypadku używania tych samych flag kompilatora. Na przykład kod wygenerowany przez program Visual Studio 2010, gdy /fp:precise (wartość domyślna) lub /fp:strict został określony, może nie propagować wartości pośredniego not-a-number (NaN) za pomocą wyrażeń poprawnie. W związku z tym niektóre wyrażenia, które dały wynik liczbowy w starszych kompilatorach, mogą teraz poprawnie wygenerować wynik NaN. Mogą być również widoczne różnice, ponieważ optymalizacje kodu włączone na /fp:fast razie korzystają z większej liczby funkcji procesora. Te optymalizacje mogą używać mniejszej liczby instrukcji, ale mogą mieć wpływ na wygenerowane wyniki, ponieważ niektóre wcześniej widoczne operacje pośrednie zostały usunięte.

Jak uzyskać identyczne wyniki

W większości przypadków zmiany zmiennoprzecinkowe w najnowszych kompilatorach i bibliotekach powodują szybsze lub bardziej poprawne zachowanie lub oba te elementy. Wydajność procesora może być nawet lepsza, gdy instrukcje SSE2 zastępują instrukcje x87. Jeśli jednak masz kod, który musi dokładnie replikować zachowanie zmiennoprzecinkowe starszego kompilatora, rozważ użycie natywnych funkcji wielowersyjności programu Visual Studio i skompiluj projekt, którego dotyczy problem, przy użyciu starszego zestawu narzędzi. Aby uzyskać więcej informacji, zobacz Używanie natywnego wielowersyjności w programie Visual Studio do kompilowania starych projektów.

Zobacz też

Uaktualnianie projektów z wcześniejszych wersji programu Visual C++
Omówienie potencjalnych problemów z uaktualnieniem (Visual C++)
Visual C++ — historia zmian w latach 2003–2015