Dlaczego wielokrotne dodawanie 0.1 pozostaje bezstratne?

Wiem, że 0.1 Liczba dziesiętna nie może być dokładnie reprezentowana skończoną liczbą binarną (Wyjaśnienie ), więc double n = 0.1 straci pewną precyzję i nie będzie dokładnie 0.1. Z drugiej strony 0.5 może być reprezentowana dokładnie dlatego, że jest 0.5 = 1/2 = 0.1b.

Powiedziawszy, że jest zrozumiałe, że dodawanie 0.1 trzy razy nie da dokładnie 0.3 więc następujący kod drukuje false:

double sum = 0, d = 0.1;
for (int i = 0; i < 3; i++)
    sum += d;
System.out.println(sum == 0.3); // Prints false, OK

Ale jak to jest, że dodawanie 0.1 pięć razy da dokładnie 0.5? Następujący kod drukuje true:

double sum = 0, d = 0.1;
for (int i = 0; i < 5; i++)
    sum += d;
System.out.println(sum == 0.5); // Prints true, WHY?

Jeśli 0.1 nie można dokładnie przedstawić, jak to jest, że dodanie go 5 razy daje dokładnie 0.5, które można dokładnie przedstawić?

Author: Peter Lawrey, 2014-09-30

3 answers

Błąd zaokrąglania nie jest przypadkowy i sposób jego implementacji stara się zminimalizować błąd. Oznacza to, że czasami błąd nie jest widoczny lub nie ma błędu.

Na przykład 0.1 nie jest dokładnie 0.1 tj. new BigDecimal("0.1") < new BigDecimal(0.1) ale 0.5 jest dokładnie 1.0/2

Ten program pokazuje prawdziwe wartości.

BigDecimal _0_1 = new BigDecimal(0.1);
BigDecimal x = _0_1;
for(int i = 1; i <= 10; i ++) {
    System.out.println(i+" x 0.1 is "+x+", as double "+x.doubleValue());
    x = x.add(_0_1);
}

Druki

0.1000000000000000055511151231257827021181583404541015625, as double 0.1
0.2000000000000000111022302462515654042363166809082031250, as double 0.2
0.3000000000000000166533453693773481063544750213623046875, as double 0.30000000000000004
0.4000000000000000222044604925031308084726333618164062500, as double 0.4
0.5000000000000000277555756156289135105907917022705078125, as double 0.5
0.6000000000000000333066907387546962127089500427246093750, as double 0.6000000000000001
0.7000000000000000388578058618804789148271083831787109375, as double 0.7000000000000001
0.8000000000000000444089209850062616169452667236328125000, as double 0.8
0.9000000000000000499600361081320443190634250640869140625, as double 0.9
1.0000000000000000555111512312578270211815834045410156250, as double 1.0

Uwaga: to 0.3 jest nieco wyłączone, ale kiedy dojdziesz do 0.4 bity muszą przesunąć się w dół, aby zmieścić się w 53-bitowym limit i błąd jest odrzucany. Ponownie pojawia się błąd dla 0.6 i 0.7, ale dla 0.8 do 1.0 błąd jest odrzucany.

Dodanie go 5 razy powinno kumulować błąd, a nie anulować.

Przyczyną błędu jest ograniczona precyzja. tj. 53-bitowe. Oznacza to, że ponieważ liczba zużywa więcej bitów, gdy staje się większa, bity muszą być upuszczane na końcu. Powoduje to zaokrąglenie, które w tym przypadku jest na Twoją korzyść.
Można uzyskać odwrotnie efekt przy uzyskaniu mniejszej liczby np. 0.1-0.0999 => 1.0000000000000286E-4 i widzisz więcej błędów niż wcześniej.

Przykładem tego jest dlaczego w Javie 6 dlaczego Matematyka.round (0.499999999999999999999) return 1 w tym przypadku utrata bitu w obliczeniach powoduje dużą różnicę w odpowiedzi.

 149
Author: Peter Lawrey,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:46:08

Poza przepełnieniem, w funkcji zmiennoprzecinkowej, x + x + x jest dokładnie dokładnie zaokrągloną (tj. najbliższą) liczbą zmiennoprzecinkową do rzeczywistej 3*x, x + x + x + x jest dokładnie 4 * x, A {[4] } jest ponownie poprawnie zaokrąglonym przybliżeniem zmiennoprzecinkowym dla 5 * x.

Pierwszy wynik dla x + x + x wynika z faktu, że x + x jest dokładny. x + x + x jest więc wynikiem tylko jednego zaokrąglenia.

Drugi wynik jest trudniejszy, jedna jego demonstracja jest omówiona tutaj (i Stephen Canon odwołuje się do innego dowodu poprzez analizę przypadku na ostatnich 3 cyfrach x). Podsumowując, 3 * x jest w tym samym binade jako 2 * x lub jest w tym samym binade co 4 * x i w każdym przypadku można wywnioskować, że błąd trzeciego dodatku anuluje błąd drugiego dodatku (pierwszy dodatek jest dokładny, jak już powiedzieliśmy).

Trzeci wynik, "x + x + x + x + x jest prawidłowo zaokrąglony", wywodzi się z drugiego w ten sam sposób, w jaki pierwszy wynika z dokładności x + x.


Drugi wynik wyjaśnia, dlaczego 0.1 + 0.1 + 0.1 + 0.1 jest dokładnie liczbą zmiennoprzecinkową 0.4: Liczby wymierne 1/10 i 4/10 są przybliżone w ten sam sposób, z tym samym względnym błędem, po przekonwertowaniu na zmiennoprzecinkową. Te liczby zmiennoprzecinkowe mają stosunek dokładnie 4 między nimi. Pierwszy i trzeci wynik pokazują, że 0.1 + 0.1 + 0.1 i 0.1 + 0.1 + 0.1 + 0.1 + 0.1 można oczekiwać, że będą miały mniej błędów, niż można by wywnioskować z naiwnej analizy błędów, ale same w sobie, odnoszą się one jedynie do odpowiednio 3 * 0.1 i 5 * 0.1, które mogą być bliskie, ale niekoniecznie identyczne z 0.3 i 0.5.

Jeśli dodasz 0.1 po czwartym dodaniu, w końcu zauważysz błędy zaokrąglania, które sprawiają, że" 0.1 dodane do siebie n razy " odbiegają od n * 0.1, a różnią się jeszcze bardziej od n/10. Jeśli miałbyś wykreślić wartości "0.1 dodane do siebie n razy" jako funkcja n, obserwowałbyś linie stałego nachylenia binad (jako gdy wynik n-tego dodawania jest przeznaczony do konkretnego binade, można oczekiwać, że właściwości dodawania będą podobne do poprzednich dodatków, które dały wynik w tym samym binade). W tym samym binade, błąd będzie rosnąć lub kurczyć. Jeśli spojrzeć na sekwencję nachylenia od binade do binade, rozpoznałbyś powtarzające się cyfry 0.1 w binarnym przez chwilę. Po tym, absorpcja zacznie mieć miejsce i Krzywa pójdzie płaska.

 46
Author: Pascal Cuoq,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-10-02 08:22:15

Systemy zmiennoprzecinkowe wykonują różne magia, w tym mają kilka dodatkowych bitów precyzji do zaokrąglania. Tak więc bardzo mały błąd z powodu niedokładnej reprezentacji 0,1 kończy się zaokrągleniem do 0,5.

Myśl o zmiennoprzecinkowej jako o świetnym, ale niedokładnym sposobie reprezentacji liczb. Nie wszystkie możliwe liczby są łatwo reprezentowane w komputerze. Irracjonalne liczby jak PI. Lub jak sqrt (2). (Symboliczne systemy matematyczne mogą je reprezentować, ale powiedziałem "łatwo".)

The wartość zmiennoprzecinkowa może być bardzo bliska, ale nie dokładna. Może być tak blisko, że można przejść do Plutona i być off o milimetry. Ale nadal nie dokładnie w sensie matematycznym.

Nie używaj zmiennoprzecinkowych, gdy musisz być dokładny, a nie przybliżony. Na przykład aplikacje księgowe chcą dokładnie śledzić określoną liczbę groszy na koncie. Liczby całkowite są do tego dobre, ponieważ są dokładne. Podstawowym problemem, na który trzeba uważać z liczbami całkowitymi, jest przepełnienie.

Używanie BigDecimal dla waluty działa dobrze, ponieważ podstawowa reprezentacja jest liczbą całkowitą, choć dużą.

Uznając, że liczby zmiennoprzecinkowe są niedokładne, nadal mają wiele zastosowań. Układy współrzędnych do nawigacji lub współrzędne w systemach graficznych. Wartości astronomiczne. Wartości naukowych. (Prawdopodobnie nie możesz znać dokładnej masy piłki do masy elektronu i tak, więc niedokładność nie ma znaczenia.)

Dla aplikacje liczące (w tym rachunkowe) używają integer. Do zliczania liczby osób, które przechodzą przez bramę, użyj int lub long.

 -1
Author: DannyB,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-10-01 20:06:29