Dzielenie int: Dlaczego wynik 1/3 == 0?

107

Pisałem ten kod:

public static void main(String[] args) {
    double g = 1 / 3;
    System.out.printf("%.2f", g);
}

Wynik to 0. Dlaczego tak jest i jak rozwiązać ten problem?

Tofiq
źródło

Odpowiedzi:

147

Dwa operandy (1 i 3) są liczbami całkowitymi, dlatego używana jest arytmetyka liczb całkowitych (tutaj dzielenie). Zadeklarowanie zmiennej wynikowej jako podwójnej powoduje po prostu niejawną konwersję po podzieleniu .

Dzielenie przez liczbę całkowitą zwraca oczywiście prawdziwy wynik dzielenia zaokrąglony do zera. Wynik funkcji 0.333...jest zatem zaokrąglany w dół do 0. (Zwróć uwagę, że procesor w rzeczywistości nie wykonuje żadnego zaokrąglania, ale nadal możesz o tym myśleć w ten sposób).

Zwróć również uwagę, że jeśli oba operandy (liczby) są podane jako zmiennoprzecinkowe; 3.0 i 1.0, a nawet tylko pierwsza , a następnie używana jest arytmetyka zmiennoprzecinkowa, co daje 0.333....

Noldorin
źródło
33
+1 i zawsze zaokrągla w dół. int i = .99999999ustawia int na 0. Dokładniej, pobiera część całkowitą, a resztę odrzuca.
Byron Whitlock
37
Zaokrągla „w kierunku zera”, które jest „w dół” dla wartości większych niż zero. (+0,9 zostaje zaokrąglone do 0, -0,9 również zostaje zaokrąglone do 0.)
Adrian Smith
@Byron: Tak, dokładnie. Nie sądzę, aby procesor faktycznie dokonywał zaokrąglania, ponieważ dzielenie jest realizowane w bardzo różny sposób, ale warto myśleć o tym w ten sposób.
Noldorin
15
Nie, bez zaokrąglania: Obcięcie
Basil Bourque
3
@BasilBourque W terminologii Java zaokrąglanieDOWN jest do zera. Zaokrąglanie w FLOORkierunku ujemnej nieskończoności.
Andreas
23

1/3 używa dzielenia liczb całkowitych, ponieważ obie strony są liczbami całkowitymi.

Potrzebujesz co najmniej jednego z nich, aby być floatlub double.

Jeśli wpisujesz wartości w kodzie źródłowym, tak jak twoje pytanie, możesz to zrobić 1.0/3; to 1.0jest podwójne.

Jeśli uzyskasz wartości z innego miejsca, możesz użyć ich (double)do przekształcenia intw double.

int x = ...;
int y = ...;
double value = ((double) x) / y;
Adrian Smith
źródło
10

Jawnie rzuć to jako plik double

double g = 1.0/3.0

Dzieje się tak, ponieważ Java używa operacji dzielenia liczb całkowitych dla 1i 3od czasu wprowadzenia ich jako stałych całkowitych.

Charlie
źródło
2

powinieneś użyć

double g=1.0/3;

lub

double g=1/3.0;

Dzielenie liczb całkowitych zwraca liczbę całkowitą.


źródło
2

Ponieważ robisz dzielenie liczb całkowitych.

Jak mówi @Noldorin, jeśli oba operatory są liczbami całkowitymi, używany jest dzielenie liczb całkowitych.

Wynik 0,33333333 nie może być reprezentowany jako liczba całkowita, dlatego do wyniku jest przypisywana tylko część całkowita (0).

Jeśli którykolwiek z operatorów to double/ float, to zostanie przeprowadzona arytmetyka zmiennoprzecinkowa. Ale będziesz miał ten sam problem, jeśli to zrobisz:

int n = 1.0 / 3.0;
Tomek
źródło
1

Ponieważ traktuje 1 i 3 jako liczby całkowite, dlatego zaokrągla wynik w dół do 0, aby był liczbą całkowitą.

Aby uzyskać wynik, którego szukasz, wyraźnie powiedz javie, że liczby są podwojone, w następujący sposób:

double g = 1.0/3.0;
DanielGibbs
źródło
1

Ustaw 1 jako zmiennoprzecinkowy i zostanie użyty podział zmiennoprzecinkowy

public static void main(String d[]){
    double g=1f/3;
    System.out.printf("%.2f",g);
}
sblundy
źródło
1

Konwersja w JAVA jest dość prosta, ale wymaga pewnego zrozumienia. Jak wyjaśniono w JLS dla operacji na liczbach całkowitych :

Jeśli operator liczby całkowitej inny niż operator przesunięcia ma co najmniej jeden operand typu long, to operacja jest wykonywana z 64-bitową precyzją, a wynik operatora numerycznego jest typu long. Jeśli drugi operand nie jest długi, jest on najpierw rozszerzany (pkt 5.1.5), aby wpisać long przez promocję liczbową (pkt 5.6).

A przykład to zawsze najlepszy sposób na przetłumaczenie JLS;)

int + long -> long
int(1) + long(2) + int(3) -> long(1+2) + long(3)

W przeciwnym razie operacja jest wykonywana z 32-bitową precyzją, a wynik operatora numerycznego jest typu int. Jeśli którykolwiek z operandów nie jest int, jest najpierw rozszerzany do typu int przez promocję liczbową.

short + int -> int + int -> int

Mały przykład użycia Eclipse, aby pokazać, że nawet dodanie dwóch shortznaków nie będzie takie łatwe:

short s = 1;
s = s + s; <- Compiling error

//possible loss of precision
//  required: short
//  found:    int

Będzie to wymagało odlewania z możliwą utratą precyzji.

To samo dotyczy operatorów zmiennoprzecinkowych

Jeśli co najmniej jeden z operandów do operatora numerycznego jest typu double, to operacja jest wykonywana przy użyciu 64-bitowej arytmetyki zmiennoprzecinkowej, a wynikiem operatora numerycznego jest wartość typu double. Jeśli drugi operand nie jest podwójnym operatorem, jest najpierw rozszerzany (§5.1.5), aby wpisać double przez promocję liczbową (§5.6).

Tak więc promocja odbywa się na float w double.

A połączenie wartości całkowitej i zmiennoprzecinkowej daje wartości zmiennoprzecinkowe, jak powiedziano

Jeśli co najmniej jeden z operandów operatora binarnego jest typu zmiennoprzecinkowego, to operacja jest operacją zmiennoprzecinkową, nawet jeśli druga jest całka.

Dzieje się tak w przypadku operatorów binarnych, ale nie w przypadku „Operatorów przypisania”, takich jak +=

Aby to udowodnić, wystarczy prosty przykład roboczy

int i = 1;
i += 1.5f;

Powodem jest to, że wykonano tutaj niejawne rzutowanie, będzie to wykonane podobnie

i = (int) i + 1.5f
i = (int) 2.5f
i = 2
AxelH
źródło
1

1 i 3 są liczbami całkowitymi, więc Java wykonuje dzielenie liczb całkowitych, którego wynikiem jest 0. Jeśli chcesz zapisać podwójne stałe, musisz napisać 1.0i 3.0.

Restart
źródło
1

Najłatwiejszym rozwiązaniem jest po prostu to zrobić

double g = ((double) 1 / 3);

To, co robi, ponieważ nie wprowadziłeś 1.0 / 3.0, pozwala ręcznie przekonwertować go na typ danych double, ponieważ Java założyła, że ​​jest to dzielenie całkowite, i zrobiłoby to, nawet jeśli oznaczałoby to zawężenie konwersji. Nazywa się to operatorem rzutowania.

Ramzi El-Jabali
źródło
1

Ja to zrobiłem.

double g = 1.0/3.0;
System.out.printf("%gf", g);

Użyj .0 podczas wykonywania podwójnych obliczeń, inaczej Java założy, że używasz liczb całkowitych. Jeśli Obliczenie wykorzystuje dowolną ilość podwójnych wartości, wynik będzie podwójną wartością. Jeśli wszystkie są liczbami całkowitymi, wynik będzie liczbą całkowitą.

BendedWills
źródło
0

(1/3) oznacza dzielenie całkowite, dlatego z tego dzielenia nie można uzyskać wartości dziesiętnej. Aby rozwiązać ten problem, użyj:

public static void main(String[] args) {
        double g = 1.0 / 3;
        System.out.printf("%.2f", g);
    }
Maruf Hossain
źródło
0
public static void main(String[] args) {
    double g = 1 / 3;
    System.out.printf("%.2f", g);
}

Ponieważ zarówno 1, jak i 3 są liczbami całkowitymi, wynik nie jest zaokrąglony, ale jest obcięty. Więc ignorujesz ułamki i bierzesz tylko całość.

Aby tego uniknąć, miej przynajmniej jedną ze swoich liczb 1 lub 3 w postaci dziesiętnej 1.0 i / lub 3.0.

Bek
źródło
0

Wypróbuj to:

public static void main(String[] args) {
    double a = 1.0;
    double b = 3.0;
    double g = a / b;
    System.out.printf(""+ g);
}
Virus Neeraj
źródło
Nie wysyłaj odpowiedzi zawierających tylko kod, dołączaj wyjaśnienie, co robi Twój kod i jak (i ​​dlaczego) rozwiązuje problem. Weź również pod uwagę, że jest to pytanie sprzed 8 lat, na które już zostały poparte odpowiedziami i czy Twoja odpowiedź ma jakąkolwiek wartość w stosunku do istniejących odpowiedzi.
Mark Rotteveel
-1

Wykonaj „double g = 1,0 / 3,0;” zamiast.

Brian Knoblauch
źródło
Po prostu ciekawy ... co jest nie tak z tą odpowiedzią? Zastosowałem to podejście, kiedy pierwszy raz napotkałem problem. Byłoby mile widziane wyjaśnienie, co jest nie tak w tym rozwiązaniu. Z góry dziękuję.
Mr. Port St Joe
@ Mr.PortStJoe Nie podaje żadnych szczegółów osobie zadającej pytanie. Patrząc na najwyżej ocenioną odpowiedź, widzimy, że wyjaśnili oni również DLACZEGO to się dzieje. Chociaż odpowiedź może być technicznie poprawna, jej przydatność może być postrzegana jako ograniczona.
Andrew T Finnell
-1

Wielu innym nie udało się wskazać prawdziwego problemu:

Operacja tylko na liczbach całkowitych rzutuje wynik operacji na liczbę całkowitą.

To z konieczności oznacza, że ​​wyniki zmiennoprzecinkowe, które mogą być wyświetlane jako liczby całkowite, zostaną obcięte (odcięcie części dziesiętnej).

Co to jest rzutowanie (rzutowanie / konwersja typów), o co pytasz?

Zależy to od implementacji języka, ale Wikipedia ma dość wszechstronny pogląd i mówi również o przymusie , który jest kluczową informacją w odpowiedzi na twoje pytanie.

http://en.wikipedia.org/wiki/Type_conversion

sova
źródło
Ta odpowiedź jest błędna. Nie ma żadnego rzutowania ani konwersji typów zaangażowanych w odchylanie oparte wyłącznie na liczbach całkowitych, np. 1/2W języku docelowym (java). Po prostu wywołujesz dzielenie liczb całkowitych, co da wynik w postaci liczby całkowitej. Rzutowanie typów jest spowodowane tylko konwersją w górę z intna a doublepodczas przypisywania.
YoYo
@YoYo Mówisz, że 0,5 to liczba całkowita? Nie sądzę, ziomek.
sova
1
ten ziomek nic nie mówił 0.5. Po prostu w Javie 1/2jest to dzielenie liczb całkowitych, które daje zerową liczbę całkowitą. Możesz przypisać zero do podwójnego, nadal będzie to zero, chociaż 0.0podwójne.
YoYo