2013-03-11 12 views
6

Dlaczego:Pokój vs dziesiętny Zaokrąglanie w C#

double dividend = 1.0; 
double divisor = 3.0; 
Console.WriteLine(dividend/divisor * divisor); 

wyjście 1.0,

ale:

decimal dividend = 1; 
decimal divisor = 3; 
Console.WriteLine(dividend/divisor * divisor); 

wyjścia 0.9999999999999999999999999999

?

Rozumiem, że 1/3 nie może być obliczona dokładnie, więc musi być pewne zaokrąglenie. Ale dlaczego Double zaokrągla odpowiedź do 1,0, ale Decimal nie?

Ponadto, dlaczego podwójne obliczenie 1.0/3.0 ma być 0,33333333333333331? Jeśli używane jest zaokrąglanie, to czy ostatnie 3 nie zostanie zaokrąglone do 0, dlaczego 1?

+2

http : //docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html –

+0

Niestety nie mam czasu na przeczytanie całego artykułu. Czy możesz wskazać mi odpowiednią sekcję, lub jeśli znasz odpowiedź, po prostu wpisz ją tutaj? –

Odpowiedz

14

Dlaczego 1/3 jako podwójne jest 0,33333333333333331

Najbliższa droga do reprezentowania 1/3 binarny jest tak: 0,0101010101 ... To samo jak seria 1/4 + (1/4)^2 + (1/4)^3 + (1/4)^4 ...

Oczywiście jest to ograniczone liczbą bitów, które można przechowywać w podwójnym. Podwójny to 64 bity, ale jednym z nich jest bit znaku, a kolejne 11 reprezentuje wykładnik (myślę o nim jak o notacji naukowej, ale w postaci binarnej). Resztę, która nazywa się mantysa lub significand, wynosi 52 bity. Załóżmy 1, aby rozpocząć, a następnie użyć dwóch bitów dla każdej kolejnej potęgi 1/4. Oznacza to, że można przechowywać: 1/4 + 1/4^2 + ... + 1/4^27 który jest 0,33333333333333331

Dlaczego mnożąc przez 3 rundy to 1

So 1/3 reprezentowane w trybie binarnym i ograniczone przez rozmiar podwójnego to: 0.010101010101010101010101010101010101010101010101010101 Nie mówię, że tak to jest przechowywane. Tak jak mówiłem, przechowujesz bity zaczynające się po 1, a używasz oddzielnych bitów dla wykładnika i znaku. Ale myślę, że warto zastanowić się, jak byś napisał to w bazie 2.

Trzymajmy się tej binarnej reprezentacji "matematyka" i zignorujmy granice wielkości podwójnej. Nie musisz tego robić w ten sposób, ale uważam, że jest to wygodne. Jeśli chcemy przyjąć to przybliżenie za 1/3 i pomnożyć przez 3, to jest to to samo co zmiana bitu do pomnożenia przez 2, a następnie dodanie tego, co zaczęliśmy. Daje nam to 1/3 * 3 = 0.111111111111111111111111111111111111111111111111111111

Ale czy może to podwójny sklep? Nie, pamiętaj, że możesz mieć tylko 52 bity mantysy po pierwszym 1, a liczba ta wynosi 54. Więc wiemy, że zostanie ona zaokrąglona w tym przypadku zaokrągla się w górę do dokładnie 1.

Dlaczego dla przecinku dostać 0,9999999999999999999999999999

Z przecinku, można uzyskać 96 bitów do reprezentowania integer z dodatkowymi bitami reprezentujący wykładnik do 28 potęg 10. Więc chociaż ostatecznie wszystko jest przechowywane jako binarne, tutaj pracujemy z potęgami 10, więc warto pomyśleć o liczbie w bazie 10.96 bitów pozwala nam wyrazić aż do 79 226 162 514 264 337 593 533 935 335, ale do 1/3 idziemy z wszystkimi 3, aż do 28 z nich możemy przesunąć na prawo od kropki dziesiętnej: 0.3333333333333333333333333333.

Pomnożenie tego przybliżenia dla 1/3 przez 3 daje nam liczbę, którą możemy dokładnie przedstawić. To tylko 28 9, wszystkie przesunięte na prawo od kropki dziesiętnej: 0.9999999999999999999999999999. Tak więc w przeciwieństwie do podwójnych nie ma w tym momencie drugiej rundy zaokrąglania.

+1

Zauważ, że dla '10m/3m' możemy użyć wszystkich 29 trójek możliwych (poniżej 79,228,162,514,264,337,593,543,950,335) (" skala "lub wykładnik jest nadal maksymalnym 28), a pomnożenie przez" 3m "wymusza zaokrąglenie (nie możemy mieć 29 dziewiątek), więc wynik zmienia się na "10.000000000000000000000000000m". Zwróć uwagę na końcowe zera. Będzie to porównywać do '10m' bez końcowych zer. –

+0

Dla przykładu z 'podwójnym', weź' (1.0/49.0) * 49.0', który nie będzie równy jednemu. Może wyglądać jak "1", jeśli zostanie wydrukowany, ale jeśli użyjesz '.ToString (" G17 ")' lub '.ToString (" R ")' zobaczysz, że nie jest dokładnie '1'. Jeśli weźmiesz jeden milion i podzielisz się o czterdzieści dziewięć i pomnożysz przez czterdzieści dziewięć ponownie, z "podwójnym" otrzymasz błąd (nie z powrotem do miliona dokładnie), natomiast z "dziesiętnym" dostaniesz milion, z kilkoma końcowych zer. –

-2

Jest to projekt typu dziesiętnego, który został zoptymalizowany pod względem dokładności w przeciwieństwie do podwójnego typu, zoptymalizowanego pod kątem niskiej dokładności, ale wyższej wydajności.

Typ wartości Decimal reprezentuje liczby dziesiętne w zakresie od dodatnich 79 226 162,514,264,337,593,543,950,335 do ujemnych 79 222 162,514,264,337,593,543,950,335.

Typ wartości dziesiętnej jest odpowiedni do obliczeń finansowych wymagających dużej liczby znaczących cyfr całkowitych i ułamkowych oraz bez błędów zaokrąglania. Typ dziesiętny nie eliminuje potrzeby zaokrąglania. Przeciwnie, minimalizuje błędy z powodu zaokrągleń. Zatem kod produkuje wyniku +0,9999999999999999999999999999 zamiast 1.

Jednym z powodów, że nieskończone dziesiętne są konieczne rozszerzenie dziesiętnych skończonych jest reprezentowanie frakcji. Używając długich podziałów, prosty podział liczb całkowitych, takich jak 1/9, staje się powtarzalnym dziesiętnym, 0.111 ..., w którym cyfry powtarzają się bez końca. Ten dziesiętny daje szybki dowód na 0.999 ... = 1. Mnożenie 9 razy 1 daje 9 na każdej cyfrze, więc 9 × 0.111 ... jest równe 0.999 ... i 9 × 1/9 równa się 1, więc 0.999 ... = 1:

+1

Nie chodzi dokładnie o dokładność, chodzi o reprezentację i zaokrąglanie. Wartość dziesiętna używa więcej bajtów do przechowywania wartości, więc otrzymuje bardziej znaczące cyfry, ale może powodować nieparzyste problemy z zaokrąglaniem, takie jak OP. – Corey