Limit int
wynosi od -2147483648 do 2147483647.
Jeśli wprowadzę
int i = 2147483648;
wtedy Eclipse wyświetli czerwone podkreślenie pod „2147483648”.
Ale jeśli to zrobię:
int i = 1024 * 1024 * 1024 * 1024;
będzie się dobrze skompilować.
public class Test {
public static void main(String[] args) {
int i = 2147483648; // error
int j = 1024 * 1024 * 1024 * 1024; // no error
}
}
Może to podstawowe pytanie w Javie, ale nie mam pojęcia, dlaczego drugi wariant nie powoduje błędów.
2147483648
: to dosłowne nie ma sensu.Odpowiedzi:
Nie ma nic złego w tym stwierdzeniu; po prostu mnożysz 4 liczby i przypisujesz je do liczby int, po prostu zdarza się, że jest przepełnienie. Różni się to od przypisywania pojedynczego literału , który byłby sprawdzany w czasie kompilacji.
To literał poza zakresem powoduje błąd, a nie przypisanie :
Z drugiej strony
long
literał skompilowałby się dobrze:Zwróć uwagę, że w rzeczywistości wynik jest nadal obliczany w czasie kompilacji, ponieważ
1024 * 1024 * 1024 * 1024
jest wyrażeniem stałym :staje się:
Zwróć uwagę, że wynik (
0
) jest po prostu ładowany i zapisywany i nie ma miejsca żadne mnożenie.Od JLS §3.10.1 (dzięki @ChrisK za poruszenie tego w komentarzach):
źródło
-1 + 1
jest to nieszkodliwe; ale1024^4
może to zaślepić ludzi z całkowicie nieoczekiwanymi wynikami, dalekimi od tego, czego oczekiwaliby widzieć. Myślę, że powinno być przynajmniej ostrzeżenie lub uwaga dla użytkownika i nie należy go po cichu ignorować.1024 * 1024 * 1024 * 1024
i2147483648
nie mają tej samej wartości w Javie.W rzeczywistości
2147483648
NIE JEST NAWET WARTOŚCIĄ (chociaż2147483648L
jest) w Javie. Kompilator dosłownie nie wie, co to jest ani jak go używać. Więc jęczy.1024
jest prawidłową liczbą int w Javie, a poprawnaint
pomnożona przez inną poprawnąint
jest zawsze poprawnaint
. Nawet jeśli nie jest to ta sama wartość, której można by się intuicyjnie spodziewać, ponieważ obliczenia będą przepełnione.Przykład
Rozważmy następujący przykład kodu:
Czy spodziewałbyś się, że spowoduje to błąd kompilacji? Teraz robi się trochę bardziej ślisko.
A co jeśli umieścimy pętlę z 3 iteracjami i pomnożymy w pętli?
Kompilator może optymalizować, ale nie może zmieniać zachowania programu w tym czasie.
Kilka informacji o tym, jak faktycznie załatwiamy tę sprawę:
W Javie i wielu innych językach liczby całkowite będą składały się ze stałej liczby bitów. Obliczenia, które nie mieszczą się w podanej liczbie bitów, zostaną przepełnione ; Obliczenie jest wykonywane w zasadzie Modulus 2 ^ 32, Jawa, po czym wartość jest przekształcany z powrotem do podpisanego całkowitej.
Inne języki lub interfejsy API używają dynamicznej liczby bitów (
BigInteger
w Javie), zgłaszają wyjątek lub ustawiają wartość na magiczną wartość, taką jak not-a-number.źródło
2147483648
NIE JEST NAWET WARTOŚCIĄ (chociaż2147483648L
jest)” naprawdę ugruntowało sprawę, którą @arshajii starał się podkreślić.1024 * 1024 * 1024 * 1024
jest traktowane, naprawdę chciałem podkreślić, że to nie to samo, co pisanie2147473648
. Istnieje wiele sposobów (i wymieniłeś kilka), na które język mógłby sobie z tym poradzić. Jest rozsądnie oddzielony i przydatny. Więc zostawię to. Wiele informacji staje się coraz bardziej potrzebnych, gdy masz wysoko sklasyfikowaną odpowiedź na popularne pytanie.Proponowane przez Ciebie zachowanie - to znaczy generowanie komunikatu diagnostycznego, gdy obliczenia generują wartość większą niż największa wartość, która może być przechowywana jako liczba całkowita - to cecha . Aby móc korzystać z dowolnej funkcji, należy ją przemyśleć, uznać za dobry pomysł, zaprojektować, określić, zaimplementować, przetestować, udokumentować i dostarczyć użytkownikom.
W przypadku Javy co najmniej jedna z rzeczy na tej liście się nie wydarzyła i dlatego nie masz tej funkcji. Nie wiem który; musiałbyś zapytać projektanta Java.
W przypadku C # wszystkie te rzeczy się wydarzyły - około czternaście lat temu - i dlatego odpowiedni program w C # wygenerował błąd od czasu C # 1.0.
źródło
Oprócz odpowiedzi arshajii chcę pokazać jeszcze jedną rzecz:
To nie przypisanie powoduje błąd, ale po prostu użycie literału . Kiedy próbujesz
zauważysz, że powoduje to również błąd kompilacji, ponieważ prawa strona nadal jest
int
literą -literal i poza zakresem.Zatem operacje z
int
-wartościami (i to w tym przypisania) mogą przepełniać się bez błędu kompilacji (i bez błędu czasu wykonania), ale kompilator po prostu nie może obsłużyć tych zbyt dużych literałów.źródło
O: Ponieważ to nie jest błąd.
Tło: mnożenie
1024 * 1024 * 1024 * 1024
doprowadzi do przepełnienia. Przepełnienie jest bardzo często błędem. Różne języki programowania powodują różne zachowania, gdy występują przepełnienia. Na przykład C i C ++ nazywają to „niezdefiniowanym zachowaniem” dla liczb całkowitych ze znakiem, a zachowanie jest zdefiniowane jako liczby całkowite bez znaku (weź wynik matematyczny, dodaj,UINT_MAX + 1
o ile wynik jest ujemny, odejmij,UINT_MAX + 1
jeśli wynik jest większy niżUINT_MAX
).W przypadku Javy, jeśli wynik operacji z
int
wartościami nie mieści się w dozwolonym zakresie, koncepcyjnie Java dodaje lub odejmuje 2 ^ 32, aż wynik znajdzie się w dozwolonym zakresie. Więc oświadczenie jest całkowicie legalne i nie jest błędne. Po prostu nie daje rezultatów, na jakie liczyłeś.Z pewnością możesz się spierać, czy to zachowanie jest pomocne i czy kompilator powinien dać ci ostrzeżenie. Osobiście powiedziałbym, że ostrzeżenie byłoby bardzo przydatne, ale błąd byłby nieprawidłowy, ponieważ jest to legalna Java.
źródło