2017-11-08 26 views
14

Czy ktoś mógłby mi powiedzieć, dlaczego te dwa obliczenia modułu dają dwa różne wyniki? Po prostu muszę winić kogoś lub coś, oprócz mnie, przez te wszystkie godziny, że straciłem ten błąd.Moduł daje zły wynik?

public void test1() 
{ 
    int stepAmount = 100; 
    float t = 0.02f; 
    float remainder = t % (1f/stepAmount); 
    Debug.Log("Remainder: " + remainder); 
    // Remainder: 0.01 

    float fractions = 1f/stepAmount; 
    remainder = t % fractions; 
    Debug.Log("Remainder: " + remainder); 
    // Remainder: 0 
} 

Korzystanie VS-2017 V15.3.5

+1

Za to, co jest warte, nie mogę tego odtworzyć na VS for Mac v7.2 (636). Obie reszty to '0.0' – InBetween

+0

Dotnetfiddle pokazuje 0 dla obu wyników: https://dotnetfiddle.net/XTHswt Wypróbowałem dokładnie ten sam kod co w linku dotnetfiddle z https://www.jdoodle.com/compile-c -Sharp-online i pokazuje problem, który opisujesz. – TyCobb

+0

Powtarzalne tutaj: https://ideone.com/JHomEl – Ryan

Odpowiedz

8

Moja Najprościej jest to, że jest to spowodowane wolności runtime musi wykonać operacji zmiennoprzecinkowych z większą dokładnością niż typy zaangażowany, a następnie wynik do obcinania precyzja typu podczas przypisywania:

Specyfikacja CLI w sekcji 12.1.3 określa dokładną dokładność dla liczb zmiennoprzecinkowych, zmiennoprzecinkowych i podwójnych, w przypadku użycia w miejscach przechowywania. Jednak pozwala na przekroczenie dokładności, gdy liczby zmiennoprzecinkowe są używane w innych lokalizacjach, takich jak stos wywoływania, wartości zwracane przez argumenty, itp. ... Do jakiej precyzji jest używana jednostka wykonawcza i sprzęt podstawowy. Ta dodatkowa precyzja może prowadzić do subtelnych różnic w ocenach zmiennoprzecinkowych między różnymi maszynami lub środowiskami wykonawczymi.

Źródło here.

W pierwszym przykładzie można t % (1f/stepAmount) być wykonywane w całości z większą precyzją niż float a następnie ścięty, gdy wynik jest przypisany do remainder, natomiast w drugim przykładzie 1f/stepAmount jest obcinany i przypisać do fractions przed operacją modułu.

Jako dlaczego czyni stepamountconst sprawia, że ​​obie operacje modalne spójne, powodem jest to, że 1f/stepamount natychmiast staje się stałą ekspresję, która jest oceniana i obcinane unosić precyzję w czasie kompilacji i nie różni się od pisania 0.01f które w istocie czyni oba przykłady równoważne.

+0

Po prostu nie wiem wystarczająco dużo, aby dobrze to skomentować, ale czy "f" w operacji '' '' '' '' '1f/stepAmount '' nie działa w trybie' float'? Także (jeszcze bardziej dziwnie?), Zrobienie 'stepamount' a' const' rozwiązuje problem. – Madmenyo

+0

zrzut ekranu wyprodukowanego IL z kodu https: // imgur.com/0fjIgIl –

+0

Myślę, że to jest poprawne. Zmieniając typy t, reszta i frakcje na podwójne w LinqPadzie, otrzymuję 'Remainder: 0.00999999955296516' dla obu operacji. –

Powiązane problemy