Dlaczego int i = 1024 * 1024 * 1024 * 1024 skompilować bez błędu?

Granica int wynosi od -2147483648 do 2147483647.

If I input

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 dobrze skompilowany.

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łędu.

 150
Author: devnull, 2014-07-10

5 answers

Nie ma nic złego w tym stwierdzeniu; po prostu mnożysz 4 Liczby i przypisujesz je do int, tak się składa, że jest przepełnienie. Jest to co innego niż przypisanie pojedynczego literału , który byłby sprawdzany w czasie kompilacji.

To jest out-of-bounds literal powoduje błąd, a nie przypisanie :

System.out.println(2147483648);        // error
System.out.println(2147483647 + 1);    // no error

Dla kontrastu long literal skompilowałby dobrze:

System.out.println(2147483648L);       // no error

Zauważ, że w rzeczywistości wynik jest nadal obliczane w czasie kompilacji, ponieważ 1024 * 1024 * 1024 * 1024 jest wyrażenie stałe:

int i = 1024 * 1024 * 1024 * 1024;

Staje się:

   0: iconst_0      
   1: istore_1      

Zauważ, że wynik (0) jest po prostu ładowany i przechowywany, i nie ma miejsca mnożenie.


Z JLS §3.10.1 (Dzięki @ ChrisK za poruszenie tego w komentarzach):

Jest to błąd w czasie kompilacji, jeśli literał dziesiętny typu int jest większy niż 2147483648 (231), lub jeśli literał dziesiętny 2147483648 pojawia się w dowolnym innym miejscu niż operand operatora unary minus (§15.15.4).

 230
Author: arshajii,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-07-14 23:30:44

1024 * 1024 * 1024 * 1024 i 2147483648 nie mają tej samej wartości w Javie.

Właściwie, 2147483648 nie jest nawet wartością(chociaż 2147483648L jest) w Javie. Kompilator dosłownie nie wie, co to jest, ani jak z niego korzystać. Więc jęczy.

1024 jest poprawną wartością int w języku Java, a poprawna int pomnożona przez inną poprawną int jest zawsze poprawną int. Nawet jeśli nie jest to ta sama wartość, której intuicyjnie oczekujesz, ponieważ obliczenia przepełnią się.

Przykład

Rozważ przykład kodu:

public static void main(String[] args) {
    int a = 1024;
    int b = a * a * a * a;
}

Czy spodziewasz się, że wygeneruje to błąd kompilacji? Teraz staje się trochę bardziej śliska.
Co zrobić, jeśli umieścimy pętlę z 3 iteracjami i pomnożony w pętli?

Kompilator może optymalizować, ale nie może zmienić zachowania programu w trakcie jego działania.


Kilka informacji o tym, jak ta sprawa jest faktycznie prowadzona:

W Javie i wielu innych językach liczby całkowite będą składać się ze stałej liczba bitów. Obliczenia, które nie mieszczą się w podanej liczbie bitów, będą przepełnione ; obliczenia są zasadniczo wykonywane moduł 2^32 w Javie, po czym wartość jest zamieniana z powrotem na signed integer.

Inne języki lub API używają dynamicznej liczby bitów (BigInteger w Javie), wywołują wyjątek lub ustawiają wartość magiczną, taką jak not-a-number.

 43
Author: Cruncher,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-07-16 18:13:47

Nie mam pojęcia, dlaczego drugi wariant nie powoduje błędu.

Zachowanie, które sugerujesz -- to jest Tworzenie komunikatu diagnostycznego, gdy obliczenia dają wartość większą niż największa wartość, która może być przechowywana w liczbie całkowitej -- jest funkcją . Aby korzystać z dowolnej funkcji, funkcja musi być pomyślana, uważana za dobry pomysł, zaprojektowana, określona, wdrożona, przetestowana, udokumentowana i wysłana do użytkowników.

Dla Javy, jedna lub więcej rzeczy na tej liście nie wydarzyło się, a zatem nie masz tej funkcji. Nie wiem który, trzeba by zapytać projektanta Javy.

Dla C#, wszystkie te rzeczy się wydarzyły-około czternaście lat temu-i tak odpowiedni program w C# wyprodukował błąd od C# 1.0.

 15
Author: Eric Lippert,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-07-14 18:54:09

Oprócz odpowiedzi arszajii chcę pokazać jeszcze jedną rzecz:

To nieprzypisanie powoduje błąd, ale po prostu użycie dosłownego . Kiedy spróbujesz

long i = 2147483648;

Zauważysz, że powoduje również błąd kompilacji, ponieważ prawa strona nadal jest literalna int i jest poza zasięgiem.

Więc operacje z int - wartościami (i to łącznie z przypisaniami) mogą przepełnić się bez błędu kompilacji( i bez błędu runtime), ale kompilator nie radzi sobie z tymi zbyt dużymi literałami.

 12
Author: piet.t,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-07-12 22:45:52

O: ponieważ to nie jest błąd.

Background: mnożenie 1024 * 1024 * 1024 * 1024 doprowadzi do przepełnienia. Przepełnienie jest bardzo często błędem. Różne języki programowania wytwarzają różne zachowania, gdy występują przepełnienia. Na przykład, C i C++ nazywają to "niezdefiniowanym zachowaniem" dla podpisanych liczb całkowitych, a zachowanie jest zdefiniowane jako niepodpisane liczby całkowite (weź wynik matematyczny, dodaj UINT_MAX + 1 tak długo, jak wynik jest ujemny, odjmij UINT_MAX + 1 tak długo, jak wynik jest większy niż UINT_MAX).

W przypadku Javy, jeśli wynik operacji z wartościami int nie mieści się w dozwolonym zakresie, koncepcyjnie Java dodaje lub odejmuje 2^32, dopóki wynik nie znajdzie się w dozwolonym zakresie. Tak więc oświadczenie jest całkowicie legalne i nie jest błędne. To po prostu nie daje rezultatu, na który możesz mieć nadzieję.

Możesz z pewnością argumentować, czy takie 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.

 4
Author: gnasher729,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-07-16 09:06:08