1. Liczby zmiennoprzecinkowe
Załóżmy, że postanowiłeś napisać najprostszy kalkulator. Albo dowolny inny program, w którym potrzebne są obliczenia (od banalnego liczenia pieniędzy po złożoną fizykę). Nie wszystko w prawdziwym życiu to liczby całkowite — i nic na to nie poradzimy!
Zatem uzbrójmy się w nowy typ danych!
W programowaniu liczby ułamkowe nazywa się też rzeczywistymi lub liczbami zmiennoprzecinkowymi (floating-point). W Javie, podobnie jak w większości języków, służą one do przechowywania nie tylko liczb całkowitych, ale też „ułamkowych” wartości: różnych 3.14, -28.57, 2.718281828...
Liczby zmiennoprzecinkowe występują w dwóch podstawowych typach:
| Typ | Przechowuje | Zakres wartości (w przybliżeniu) | Precyzja | Typowy rozmiar |
|---|---|---|---|---|
|
Liczby | ±1.5 × 10-45 ... ±3.4 × 1038 | ~7 cyfr po przecinku | 4 bajty |
|
Liczby | ±5.0 × 10-324 ... ±1.7 × 10308 | ~15–16 cyfr po przecinku | 8 bajtów |
Typ float
Typ float wziął nazwę od floating-point number — liczby zmiennoprzecinkowej. Liczby rzeczywiste to pojęcie z matematyki, mają określone własności. Komputer ma jednak wiele ograniczeń. Dlatego nie do końca poprawnie jest nazywać liczby ułamkowe w Javie rzeczywistymi. Używa się określenia „liczby zmiennoprzecinkowe”.
Typ float zwykle przechowuje 7 cyfr znaczących (np. 0.1234567), wykładnik dziesiątki i zajmuje 4 bajty pamięci. To bardzo mało do dokładnych obliczeń, dlatego szybko przeszło się na liczby o podwójnej precyzji.
Typ double
Typ double zawdzięcza nazwę podwójnej (double) precyzji. Zajmuje w pamięci 8 bajtów (2 razy więcej niż float) i może przechowywać do 15 cyfr znaczących: 0.123456789012345. To w zupełności wystarcza dla większości obliczeń na liczbach ułamkowych, dlatego double jest podstawowym typem do przechowywania liczb zmiennoprzecinkowych w Javie.
W tym wykładzie główny akcent położymy na typie double: domyślnie jest on polecany dla wszystkich „zwykłych” liczb ułamkowych. Dalej omówimy też typ float.
2. Deklaracja i inicjalizacja zmiennych typu double
Wszystko jak z int — tylko teraz używamy double.
// Deklarujemy zmienną i przypisujemy jej wartość Pi
double pi = 3.1415926;
// Można zadeklarować także bez inicjalizacji
double averageSalary;
averageSalary = 91234.56;
// Można wykonywać obliczenia!
double pizzaPieces = 8;
double friends = 3;
double piecesPerFriend = pizzaPieces / friends; // 2.666... (a nie 2)
Cechy składni:
- Jako separator dziesiętny używa się kropki (3.14). Jeśli użyjesz przecinka — dostaniesz błąd kompilacji!
- Ściśle rzecz biorąc, zapisując double d = 3;, nie otrzymasz błędu — typy zostaną automatycznie dopasowane (liczba całkowita zamienia się na „zmiennoprzecinkową” bez strat).
3. Wejście i wyjście liczb zmiennoprzecinkowych za pomocą Scanner
Najpierw spróbujmy wypisać liczbę ułamkową:
double amount = 42.75;
System.out.println(amount); // Wypisze: 42.75
Wszystko w porządku! A jeśli dodamy tekst:
System.out.println("Na twoim koncie: " + amount + " euro."); // Na twoim koncie: 42.75 euro.
Wprowadzanie z klawiatury
Do wczytania double należy użyć specjalnej metody klasy Scanner: console.nextDouble().
Scanner console = new Scanner(System.in);
System.out.println("Podaj temperaturę na zewnątrz:");
double temperature = console.nextDouble(); // Od razu wczytujemy double
System.out.println("Na zewnątrz jest: " + temperature + " stopni.");
4. Typ double w działaniu: arytmetyka
Wszystkie standardowe operacje (+, -, *, /) działają tak samo jak dla int:
double distance = 100.5;
double time = 2.0;
double speed = distance / time; // 50.25
System.out.println("Średnia prędkość: " + speed); // Średnia prędkość: 50.25
I to cała arytmetyka. Jedyna różnica: wynik dzielenia — zawsze liczba ułamkowa, jeśli przynajmniej jeden z operandów to double.
Porównanie z int
int a = 5, b = 2;
System.out.println(a / b); // 2 (reszta jest odrzucana)
double aa = 5, bb = 2;
System.out.println(aa / bb); // 2.5
5. Typowe błędy i osobliwości przy pracy z double
Błąd konwersji wejścia
Klasyczna sytuacja: użytkownik wpisuje 3,14 — a program oczekuje 3.14. W Javie metoda Scanner.nextDouble() kieruje się bieżącą lokalizacją: w lokalizacji rosyjskiej/niemieckiej przecinek jest dopuszczalny, w angielskiej potrzebna jest kropka. W razie potrzeby ustaw Scanner na odpowiednią lokalizację lub wczytaj ciąg i sparsuj ręcznie.
// To spowoduje problem w Locale.US, jeśli wpisano "3,14"
double value = console.nextDouble();
Błąd „niedokładności” liczb w komputerze
Tu zwykle pojawia się lekkie zdziwienie:
double x = 0.1 + 0.2;
System.out.println(x); // Hm... 0.30000000000000004
Gratulacje, zetknąłeś się z „magią” reprezentacji liczb ułamkowych wewnątrz komputera. Chodzi o to, że wielu liczb nie da się dokładnie przedstawić w systemie binarnym. Zwykle nie jest to krytyczne dla większości aplikacji, ale ma znaczenie w finansach i naukach ścisłych.
6. Ważne: double i int — konwersja automatyczna i jawna
Zdarza się, że dodajesz liczbę całkowitą i ułamkową albo przypisujesz int do zmiennej double — błędów nie będzie:
int i = 2;
double d = i; // Wszystko ok!
System.out.println(d); // 2
double dd = 3.7;
int ii = (int) dd; // Trzeba jawnie rzutować typ double na typ int!
System.out.println(ii); // 3, część ułamkowa została odcięta
Często to zaskakuje — dlaczego po rzutowaniu część ułamkowa zniknęła? Po prostu dlatego, że typ int nie potrafi przechowywać ułamków (wszystko, co po kropce, znika na zawsze).
Więcej o konwersji double do int i operatorze (int) w następnej lekcji.
7. Formatowane wyjście: ładnie wypisujemy double
Często domyślnie double jest wypisywane z masą zbędnych zer. Można sformatować wyjście:
double temp = 23.56789;
System.out.println(temp); // 23.56789
// 2 miejsca po przecinku
System.out.println(String.format("%.2f", temp)); // 23.57
// 1 miejsce po przecinku
System.out.println(String.format("%.1f%n", temp)); // 23.6
| Format | Wynik | Opis |
|---|---|---|
|
23.57 | liczba z 2 miejscami po przecinku |
|
23.6 | liczba z 1 miejscem po przecinku |
8. Typowe błędy przy pracy z float i double
Błąd nr 1: niejawna konwersja double do float
float f = 1.23; // Błąd!
Kompilator będzie niezadowolony: „Próbujesz włożyć double do float — to może prowadzić do utraty precyzji!” Zawsze dodawaj sufiks f.
Błąd nr 2: zapomniano, że dzielenie dwóch int daje wynik typu int
int a = 7, b = 2;
double result = a / b; // 3.0, a nie 3.5
Aby uzyskać część ułamkową, jawnie skonwertuj przynajmniej jeden operand:
double result = (double) a / b; // 3.5
Błąd nr 3: porównywanie liczb ułamkowych
Nie porównuj liczb zmiennoprzecinkowych na równość operatorem ==. Używaj porównania z niewielką tolerancją (epsilon).
Błąd nr 4: utrata precyzji w float
Nie przechowuj dużych liczb ani bardzo dokładnych wartości w float — mogą się „zepsuć” lub utracić istotne cyfry.
GO TO FULL VERSION