Niedokładne obliczenia w Java.

0
public class CalNaMetr2 
{
	public static void main(String args[])
	{
		double cal, metr;
		int counter;
		int pentla;
		counter = 0;		
		metr = 0;

		
		for (pentla = 1 ; pentla <= 50; pentla++)
		{
			metr=metr+0.2;
			cal = metr * 39.37;
			
			counter++;
		if(counter == 5 ) 
		{
				if (metr == 1)
				{
					System.out.println(metr +" metr to " +cal  + " cali. ");
				}
				else
				{
					if (Math.round(metr) == 2 | Math.round(metr) == 3 | Math.round(metr) == 4 )
					{
					System.out.println(Math.round(metr) +" metry to " +cal  + " cali. ");
					}
					else
					{
						System.out.println(metr +" metrów to " +cal  + " cali. ");
					}
				}
				counter = 0;
		}	
		else 
		{
			System.out.println(metr +" metra to " +cal  + " cali. ");
		}
		


			
			 	
			}
				
		


		}
	}
	
	

Pierdółka w Java. Co ma robić każdy widzi... Problem pojawia się z liczbami zmiennoprzecinkowymi. Efekt jest trochę inny niż możnaby się spodziewać:

0.2 metra to 7.874 cali. 
0.4 metra to 15.748 cali. 
0.6000000000000001 metra to 23.622000000000003 cali. 
0.8 metra to 31.496 cali. 
1.0 metr to 39.37 cali. 
1.2 metra to 47.24399999999999 cali. 
1.4 metra to 55.117999999999995 cali. 
1.5999999999999999 metra to 62.99199999999999 cali. 
1.7999999999999998 metra to 70.86599999999999 cali. 
2 metry to 78.73999999999998 cali. 
2.1999999999999997 metra to 86.61399999999999 cali. 
2.4 metra to 94.48799999999999 cali. 
2.6 metra to 102.362 cali. 
2.8000000000000003 metra to 110.236 cali. 
3 metry to 118.11000000000001 cali. 
3.2000000000000006 metra to 125.98400000000002 cali. 
3.400000000000001 metra to 133.85800000000003 cali. 
3.600000000000001 metra to 141.73200000000003 cali. 
3.800000000000001 metra to 149.60600000000002 cali. 
4 metry to 157.48000000000002 cali. 
4.200000000000001 metra to 165.35400000000004 cali. 
4.400000000000001 metra to 173.22800000000004 cali. 
4.600000000000001 metra to 181.10200000000003 cali. 
4.800000000000002 metra to 188.97600000000006 cali. 
5.000000000000002 metrów to 196.85000000000005 cali. 
5.200000000000002 metra to 204.72400000000007 cali. 
5.400000000000002 metra to 212.59800000000007 cali. 
5.600000000000002 metra to 220.47200000000007 cali. 
5.8000000000000025 metra to 228.3460000000001 cali. 
6.000000000000003 metrów to 236.22000000000008 cali. 
6.200000000000003 metra to 244.0940000000001 cali. 
6.400000000000003 metra to 251.9680000000001 cali. 
6.600000000000003 metra to 259.8420000000001 cali. 
6.800000000000003 metra to 267.7160000000001 cali. 
7.0000000000000036 metrów to 275.59000000000015 cali. 
7.200000000000004 metra to 283.4640000000001 cali. 
7.400000000000004 metra to 291.33800000000014 cali. 
7.600000000000004 metra to 299.21200000000016 cali. 
7.800000000000004 metra to 307.0860000000001 cali. 
8.000000000000004 metrów to 314.9600000000001 cali. 
8.200000000000003 metra to 322.8340000000001 cali. 
8.400000000000002 metra to 330.7080000000001 cali. 
8.600000000000001 metra to 338.58200000000005 cali. 
8.8 metra to 346.456 cali. 
9.0 metrów to 354.33 cali. 
9.2 metra to 362.20399999999995 cali. 
9.399999999999999 metra to 370.0779999999999 cali. 
9.599999999999998 metra to 377.9519999999999 cali. 
9.799999999999997 metra to 385.82599999999985 cali. 
9.999999999999996 metrów to 393.6999999999998 cali. 

Problem tkwi w dziwnym liczeniu Javy[1.5999999999999999 metra to 62.99199999999999 cali. <- wtf? .599999 ?!] Nie ma cholernych 2 metrów(jak użyłem round to są ; ) ), tylko 1.999999...98. Można to jakoś łatwo zaokrąglić, ale co z naprawdę dużymi obliczeniami ? Czy takie błędy mogą później się nawarstwiać i eskalować do zaburzenia wyników? Problem wynika z wirtualnego środowiska i bezpośredniej zamiany obliczeń na kod maszynowy (dwójkowy, da ? ), ale jak zapobiegać takim bukom ? Zaokrąglać wszystkie wyniki ? Niestety to nie ma sensu przy obliczeniach z liczbami typu: 3.32548612354 czy 0.1313131278, bo zaokrąglać tu nie wolno...

Tak, jestem nowy w te klocki. Łopatologicznie proszę o jakieś wytłumaczenie.

1

normalne przy liczbach zmiennoprzecinkowych i tak - te błędy się nawarstwiają i występują w każdym języku programowania
rozwiązaniem jest ustawienie potrzebnej dokładności - na przykład do 5 miejsca po przecinku i do tej wartości zaokrąglać wartości (właściwie wystarczy do funkcji pokazującej zazwyczaj przekazać liczbę miejsc po przecinku która ma się wyświetlić)
można też obliczenia prowadzić na typach stałoprzecinkowych, lub całkowitych - np w przypadku obliczeń na pieniądzach liczyć na groszach zamiast na złotówkach...

1

Odpowiedź brzmi: tak, obliczenia zmiennoprzecinkowe w komputerach są ZŁE i trzeba uważać jak się z nich korzysta i trzeba rozumieć że to nie są liczby rzeczywiste! Masz ograniczoną dokładność liczby zmiennoprzecinkowej i tyle. Nie da sie tak zrobić zeby ci komputer przechowywał liczby o nieograniczonej dokładności, bo musiałby zużywać nieskończenie wiele pamięci. Problem pojawia się z ułamkami okresowymi na przykład -> 1/10 zapisana binarnie jest liczbą okresową tak samo jak dziesiętne 1/3 i komputer zapamięta tylko n pierwszych bitów tej liczby i siłą rzeczy zamiast 0.1 uzyskasz 0.0999999999
Tak, oczywiście te błędy będą się nawarstwiać. Są nawet przykłady bardzo prostych funkcji rekurencyjnych które tracą dokładność niemalże momentalnie, żeby studenci zobaczyli w czym rzecz.
Nie da się temu za bardzo "zapobiegać" :P Wnioskiem 2 semestrów metor numerycznych na studiach bylo to żeby unikać jak ognia liczenia czegoś za pomocą komputera, szczególnie jeśli chcemy używać floatów/doubli.

1

można też na przykład przechowywać licznik i mianownik jako liczby całkowite i przy operacjach na przykład mnożenia mnożyć je przez siebie a dopiero przy wyświetlaniu wykonywać operację licznik/mianownik - dzięki temu błędy zaokrągleń się nie kumulują a pojawiają tylko małe, zazwyczaj nieznaczące błędy przy ostatniej operacji
ogólnie to nie jest jakiś problem nie do przeskoczenia i zawsze jest jakieś wyjście ale każdy powinien mieć świadomość że coś takiego istnieje

0

No to teraz bym ładnie poprosił o skierowanie mnie na jakiś ładny kurs : )

No i oczywiście cel... **Czyli do zdania jakiego certyfikatu dążyć jako nowy programista Javy ? **

(Javke ciamkam eclipsem, mam "Java przewodnik dla początkujących" Hebert Schildt wyd. Helion wyd V i prócz paru błędów typowo stylistycznych muszę pochwalić cegłę, bo bardzo bardzo dobrze wszystko jest opisane, jestem zarejestrowany na knowledgeblackbelt, czy ktoś by coś dodał ?)

1

Dziedzina matematyki, która zajmuje się przystosowywaniem algorytmów do obliczeń na liczbach o skończonej precyzji to Analiza numeryczna. W przypadku liczenia na liczbach skończonej precyzji, w każdym kroku może nastąpić błąd zaokrąglenia. Numeryczna stabilność algorytmu to właściwość, dzięki której kumulowanie się tych błędów jest zminimalizowane.

1 użytkowników online, w tym zalogowanych: 0, gości: 1