Zmienny i podwójny typ danych w Javie

220

Typ danych zmiennoprzecinkowych to 32-bitowy zmiennoprzecinkowy IEEE 754 o pojedynczej precyzji, a podwójny typ danych to 64-bitowy zmiennoprzecinkowy IEEE 754 o podwójnej precyzji.

Co to znaczy? A kiedy powinienem używać float zamiast double lub vice versa?

Lew
źródło
8
Powinieneś używać liczb zmiennoprzecinkowych zamiast podwójnych, gdy użycie pamięci jest krytyczne. Jeśli potrzebujesz bardziej precyzyjnych obliczeń, użyj podwójnych.
Everv0id,
12
@ Everv0id: Nie jestem pewien żadnej sytuacji, w której pamięć byłaby tak napięta, że ​​trzeba poświęcić dokładność dla przestrzeni. (Używasz Javy , na miłość boską ...) Mogą wystąpić sytuacje, w których jest ona wymagana, ale w mojej praktyce rzadko ją widziałem. Jeśli chcesz wyjaśnić, dlaczego uważasz, że jest to dobry pomysł, udzielenie odpowiedzi na przykład byłoby godnym dodatkiem.
Makoto,
5
@Makoto, właściwie nigdy nie używałem pływaków, tylko podwaja się. Ale mogą istnieć aplikacje (teoretycznie), które powinny przechowywać duże liczby liczb zmiennoprzecinkowych, więc użycie pamięci 2x może być krytyczne. Teoretycznie ofc; w praktyce zawsze możesz kupić kolejny serwer .
Everv0id,
3
Użyłem 4-bajtowych, a nawet 2-bajtowych liczb o stałej precyzji, aby zaoszczędzić pamięć, ale chyba, że ​​masz ich miliardy, raczej nie warto. Czas, który zajmuje napisanie „podwójnego” zamiast „zmiennoprzecinkowego” (ma jeszcze jedną literę), jest wart 1000x więcej niż wykorzystana dodatkowa pamięć, ale jeśli użycie doublezamiast floatratuje cię przed błędem związanym z precyzją, warto .
Peter Lawrey,

Odpowiedzi:

259

Strona Wikipedii na nim jest dobrym miejscem do rozpoczęcia.

Podsumowując:

  • floatjest reprezentowany w 32 bitach, z 1 bitem znaku, 8 bitami wykładnika i 23 bitami znaczenia (lub co wynika z liczby notacji naukowej: 2,33728 * 10 12 ; 33728 jest znaczeniem).

  • double jest reprezentowany w 64 bitach, z 1 bitem znaku, 11 bitami wykładnika i 52 bitami znaczenia.

Domyślnie Java używa doubledo reprezentowania liczb zmiennoprzecinkowych (więc literał 3.14jest wpisany double). Jest to również typ danych, który da ci znacznie większy zakres liczb, więc zdecydowanie zachęcam do jego użycia float.

Mogą istnieć pewne bibliotek, które rzeczywiście zmusić wykorzystania float, ale w ogóle - o ile można zagwarantować, że wynik będzie na tyle mała, aby zmieścić się w float„s przepisaną zakres , to najlepiej zdecydować o double.

Jeśli potrzebujesz dokładności - na przykład nie możesz mieć niedokładnej wartości dziesiętnej (np. 1/10 + 2/10) Lub robisz coś z walutą (na przykład reprezentującą 10,33 USD w systemie), użyj opcji BigDecimal, która może obsługiwać dowolna precyzja i elegancka obsługa takich sytuacji.

Makoto
źródło
4
Czy w podanym przykładzie nie jest 233728 == mantysa? Mam na myśli, gdzie jeszcze jest przechowywana część całkowita?
JaLoveAst1k
1
@ mathguy54: W notacji naukowej 2 będzie liczbą całkowitą, a 0,33728 to mantysa. Oto odniesienie do tego.
Makoto
5
Szukałem informacji o liczbach zmiennoprzecinkowych i podwójnych i znalazłem to i musiałem skomentować: jeśli robisz coś z walutą, która nie wymaga ułamków ułamkowych, użycie BigDecimal jest śmieszne. Wspólną walutą są dane dyskretne, dlatego powinieneś używać liczb całkowitych. (Jest to jeden z najczęstszych błędów popełnianych przez młodych programistów - ponieważ używamy a. Do oddzielania dolarów od centów, uważają, że jest to wartość zmiennoprzecinkowa. To nie jest.)
Trixie Wolf,
2
@TrixieWolf, czy możesz być bardziej szczegółowy, czy zaproponowałeś użycie dwóch liczb całkowitych (części całkowite i dziesiętne)? A ty mówisz o wspólnej walucie, a co z resztą? Niektóre kwoty są obliczane z dokładnością do 6 miejsc po przecinku, więc nie można tak po prostu *100. Proszę, masz
rację
9
@AxelH Z wyjątkiem środków obliczeń finansowych, w których mogą istnieć ułamkowe centy, pieniądze są zawsze dyskretne. Do przechowywania danych użyłbyś jednego typu liczby całkowitej. Tak więc 5,34 USD będzie przechowywane jako 534. Część dolara to val / 100 w matematyce liczb całkowitych, a centy to val% 100 w matematyce liczb całkowitych, gdzie% odnosi się do pozostałej operacji. W przypadku pieniędzy z większą liczbą miejsc po przecinku należy je nadal przechowywać jako integralne, ponieważ są dyskretne. Nawet jeśli nie jest dyskretny, często będziesz chciał wycofać się z dyskretnego magazynu przez większość czasu, ponieważ jest precyzyjny, aby nie stracić pieniędzy na błędy zaokrąglania.
Trixie Wolf,
72

Pływak daje ci ok. Dokładność 6-7 cyfr dziesiętnych, podczas gdy liczba podwójna daje ok. 15–16. Również zakres liczb jest większy dla podwójnego.

Podwójne potrzebuje 8 bajtów przestrzeni dyskowej, a liczba zmiennoprzecinkowa potrzebuje tylko 4 bajtów.

Henz
źródło
13

Liczby zmiennoprzecinkowe, znane również jako liczby rzeczywiste, są używane podczas oceny wyrażeń wymagających precyzji ułamkowej. Na przykład obliczenia, takie jak pierwiastek kwadratowy, lub transcendentalne, takie jak sinus i cosinus, dają wartość, której precyzja wymaga typu zmiennoprzecinkowego. Java implementuje standardowy (IEEE – 754) zestaw typów i operatorów zmiennoprzecinkowych. Istnieją dwa rodzaje typów zmiennoprzecinkowych, zmiennoprzecinkowe i podwójne, które reprezentują odpowiednio liczby o pojedynczej i podwójnej precyzji. Ich szerokość i zakresy pokazano tutaj:


   Name     Width in Bits   Range 
    double  64              1 .7e308 to 1.7e+308
    float   32              3 .4e038 to 3.4e+038


pływak

Liczba zmiennoprzecinkowa określa wartość pojedynczej precyzji, która wykorzystuje 32 bity pamięci. Pojedyncza precyzja jest szybsza na niektórych procesorach i zajmuje o połowę mniej miejsca niż podwójna precyzja, ale stanie się nieprecyzyjna, gdy wartości będą bardzo duże lub bardzo małe. Zmienne typu float są przydatne, gdy potrzebujesz komponentu ułamkowego, ale nie wymagają dużego stopnia precyzji.

Oto kilka przykładowych deklaracji zmiennych zmiennoprzecinkowych:

float hightemp, lowtemp;


podwójnie

Podwójna precyzja, jak wskazuje podwójne słowo kluczowe, używa 64 bitów do przechowywania wartości. Podwójna precyzja jest w rzeczywistości szybsza niż pojedyncza precyzja w niektórych nowoczesnych procesorach zoptymalizowanych pod kątem szybkich obliczeń matematycznych. Wszystkie transcendentalne funkcje matematyczne, takie jak sin (), cos () i sqrt (), zwracają podwójne wartości. Kiedy trzeba zachować dokładność w wielu obliczeniach iteracyjnych lub manipulować liczbami o dużej wartości, podwójny wybór jest najlepszym wyborem.

Ye Win
źródło
Ta odpowiedź jasno wyjaśnia, kiedy powinniśmy używać float i double. Dlaczego nie?
Wygraj
8
Żadne z tych typów floatani doublenajlepiej nie są używane dla waluty w Javie, ponieważ otwierają możliwość zaokrąglania błędów. Ten artykuł zawiera bardziej szczegółowe informacje: javapractices.com/topic/TopicAction.do?Id=13
PPartisan
1
„float może być przydatny przy reprezentowaniu dolarów i centów”. - nie nie nie nie nie nie nie. Nigdy, nigdy nie przechowuj waluty jako liczby zmiennoprzecinkowe.
ograniczenie aktywności
2

Wydaje się, że Java ma jednak tendencję do używania podwójnego do obliczeń:

W przypadku, gdy program, który napisałem dzisiaj, metody nie działały, gdy użyłem float, ale teraz działają świetnie, kiedy podstawiłem float na double (w IDE NetBeans):

package palettedos;
import java.util.*;

class Palettedos{
    private static Scanner Z = new Scanner(System.in);
    public static final double pi = 3.142;

    public static void main(String[]args){
        Palettedos A = new Palettedos();
        System.out.println("Enter the base and height of the triangle respectively");
        int base = Z.nextInt();
        int height = Z.nextInt();
        System.out.println("Enter the radius of the circle");
        int radius = Z.nextInt();
        System.out.println("Enter the length of the square");
        long length = Z.nextInt();
        double tArea = A.calculateArea(base, height);
        double cArea = A.calculateArea(radius);
        long sqArea = A.calculateArea(length);
        System.out.println("The area of the triangle is\t" + tArea);
        System.out.println("The area of the circle is\t" + cArea);
        System.out.println("The area of the square is\t" + sqArea);
    }

    double calculateArea(int base, int height){
        double triArea = 0.5*base*height;
        return triArea;
    }

    double calculateArea(int radius){
        double circArea = pi*radius*radius;
        return circArea;
    }

    long calculateArea(long length){
        long squaArea = length*length;
        return squaArea;
    }
}
Raymond Wachaga
źródło
Miałem dzisiaj ten sam problem. Co może być przyczyną tego błędu?
Shachi
2

To da błąd:

public class MyClass {
    public static void main(String args[]) {
        float a = 0.5;
    }
}

/MyClass.java:3: błąd: typy niezgodne: możliwa konwersja stratna z podwójnej na zmiennoprzecinkową zmiennoprzecinkową a = 0,5;

To zadziała idealnie dobrze

public class MyClass {
    public static void main(String args[]) {
        double a = 0.5;
    }
}

Będzie to również działać idealnie dobrze

public class MyClass {
    public static void main(String args[]) {
        float a = (float)0.5;
    }
}

Powód : Java domyślnie przechowuje liczby rzeczywiste jako podwójne, aby zapewnić większą precyzję.

Podwójne zajmuje więcej miejsca, ale bardziej precyzyjne podczas obliczeń, a liczba zmiennoprzecinkowa zajmuje mniej miejsca, ale mniej precyzyjne.

Himanshu Singh
źródło
1

Zgodnie ze standardami IEEE, float jest 32-bitową reprezentacją liczby rzeczywistej, a double jest reprezentacją 64-bitową.

W programach Java zwykle widzimy użycie podwójnego typu danych. Ma to na celu uniknięcie przepełnienia, ponieważ zakres liczb, które można dostosować za pomocą podwójnego typu danych, jest większy niż zakres, gdy używana jest liczba zmiennoprzecinkowa.

Również wtedy, gdy wymagana jest wysoka precyzja, zaleca się stosowanie podwójnego. Niewiele metod bibliotecznych, które zostały zaimplementowane dawno temu, nadal wymaga użycia typu danych zmiennoprzecinkowych jako obowiązkowej (tylko dlatego, że zostało zaimplementowane przy użyciu wartości zmiennoprzecinkowych, nic więcej!).

Ale jeśli jesteś pewien, że twój program wymaga małych liczb i nie wystąpi przepełnienie przy użyciu float, wówczas użycie float znacznie poprawi złożoność przestrzeni, ponieważ floaty wymagają połowy pamięci, jak wymaga podwójnie.

Rubal
źródło
0

Ten przykład ilustruje, jak wyodrębnić znak (najbardziej lewy bit), wykładnik potęgowy (8 kolejnych bitów) i mantysę (23 skrajne prawe bity) z pływaka w Javie.

int bits = Float.floatToIntBits(-0.005f);
int sign = bits >>> 31;
int exp = (bits >>> 23 & ((1 << 8) - 1)) - ((1 << 7) - 1);
int mantissa = bits & ((1 << 23) - 1);
System.out.println(sign + " " + exp + " " + mantissa + " " +
  Float.intBitsToFloat((sign << 31) | (exp + ((1 << 7) - 1)) << 23 | mantissa));

To samo podejście można zastosować do podwójnych (wykładnik 11-bitowy i 52-bitowa mantysa).

long bits = Double.doubleToLongBits(-0.005);
long sign = bits >>> 63;
long exp = (bits >>> 52 & ((1 << 11) - 1)) - ((1 << 10) - 1);
long mantissa = bits & ((1L << 52) - 1);
System.out.println(sign + " " + exp + " " + mantissa + " " +
  Double.longBitsToDouble((sign << 63) | (exp + ((1 << 10) - 1)) << 52 | mantissa));

Źródło: http://sj.github.io/java-float/

acohen
źródło
0

Powinieneś używać podwójnego zamiast zmiennoprzecinkowego do precyzyjnych obliczeń, i zmiennoprzecinkowego zamiast podwójnego przy użyciu mniej dokładnych obliczeń. Liczba zmiennoprzecinkowa zawiera tylko liczby dziesiętne, ale liczba podwójna zawiera liczbę zmiennoprzecinkową o podwójnej precyzji IEEE754, co ułatwia dokładniejsze przechowywanie i obliczanie liczb. Mam nadzieję że to pomoże.

boi yeet
źródło
0

W regularnych obliczeniach programistycznych nie używamy liczb zmiennoprzecinkowych. Jeśli upewnimy się, że zakres wyników mieści się w zakresie typu danych zmiennoprzecinkowych, możemy wybrać typ danych zmiennoprzecinkowych w celu oszczędzania pamięci. Zasadniczo używamy podwójnie z dwóch powodów:

  • Jeśli chcemy użyć liczby zmiennoprzecinkowej jako typu danych zmiennoprzecinkowych, wówczas wywołujący metodę musi jawnie przyrostek F lub f, ponieważ domyślnie każda liczba zmiennoprzecinkowa jest traktowana jako podwójna. Zwiększa obciążenie programisty. Jeśli użyjemy liczby zmiennoprzecinkowej jako podwójnego typu danych, nie musimy dodawać żadnego przyrostka.
  • Float to typ danych o pojedynczej precyzji, co oznacza, że ​​zajmuje 4 bajty. Dlatego w dużych obliczeniach nie uzyskamy pełnego wyniku. Jeśli wybierzemy podwójny typ danych, zajmuje on 8 bajtów i otrzymamy pełne wyniki.

Zarówno zmiennoprzecinkowe, jak i podwójne typy danych zostały zaprojektowane specjalnie do obliczeń naukowych, w których dopuszczalne są błędy aproksymacyjne. Jeśli najważniejsza jest dokładność, zaleca się stosowanie klasy BigDecimal zamiast typów zmiennoprzecinkowych lub podwójnych. Źródło: - Zmienne i podwójne typy danych w Javie

Rocco Jerry
źródło