İnt i = 1024 * 1024 * 1024 * 1024 neden hatasız derleniyor?


152

Sınırı int-2147483648 ile 2147483647 arasındadır.

Girersem

int i = 2147483648;

Eclipse "2147483648" altında kırmızı bir alt çizgi isteyecektir.

Ama bunu yaparsam:

int i = 1024 * 1024 * 1024 * 1024;

iyi derlenecektir.

public class Test {
    public static void main(String[] args) {        

        int i = 2147483648;                   // error
        int j = 1024 * 1024 * 1024 * 1024;    // no error

    }
}

Belki Java'da temel bir soru, ama ikinci varyantın neden hata üretmediği hakkında hiçbir fikrim yok.


10
Derleyici normalde hesaplamayı bir optimizasyon olarak tek bir değere "daraltırsa bile", sonuç taşma olursa, hiçbir optimizasyon programın davranışını değiştirmemelidir.
Hot Licks

1
Ve bu olamaz yorumlamak 2147483648: Bu değişmez bir anlam ifade etmiyor.
Denys Séguret

1
Ve Java tamsayı taşmalarını bildirmez - işlem sessizce "başarısız" olur.
Hot Licks

5
@JacobKrall: C #, kontrol edilenin açık olup olmadığına bakılmaksızın bunu bir kusur olarak rapor edecektir; yalnızca sabit ifadelerden oluşan tüm hesaplamalar, denetlenmeyen bir bölge içinde değilse otomatik olarak kontrol edilir.
Eric Lippert

54
Size StackOverflow "neden olmasın" sorular sormaktan vazgeçirmek; cevaplamak zor. “Neden olmasın” sorusu, dünyanın açıkça öyle olmadığı bir yol olması gerektiğini ve bunun böyle olması için iyi bir neden olması gerektiğini varsayar. Bu varsayım neredeyse hiçbir zaman geçerli değildir. Daha kesin bir soru "şartnamenin hangi bölümünde sabit tamsayı aritmetiğinin nasıl hesaplandığını açıklar?" veya "tamsayı taşmaları Java'da nasıl ele alınır?"
Eric Lippert

Yanıtlar:


233

Bu ifadede yanlış bir şey yok; sadece 4 rakamı çarpıp bir int'e atarsanız, bir taşma olur. Bu, derleme zamanında sınır denetlenecek tek bir hazır bilgi atamaktan farklıdır .

Bu out-of-sınırları olduğunu değişmezi konusu hatayı değil, neden atama :

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

Bunun aksine bir longdeğişmez para cezası derler:

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

Aslında, sonuç, unutmayın edilir çünkü hala derleme zamanında hesaplanan 1024 * 1024 * 1024 * 1024bir olan sabit ifadesi :

int i = 1024 * 1024 * 1024 * 1024;

dönüşür:

   0: iconst_0      
   1: istore_1      

Sonuçun ( 0) basitçe yüklendiğine ve saklandığına ve çarpma gerçekleşmediğine dikkat edin.


Gönderen JLS §3.10.1 (yorumlar kısmında açtığın için @ChrisK sayesinde):

Ondalık bir ondalık harf türü (2 31 ) ' intden büyükse veya ondalık harf değişkeni tekli eksi operatörünün işleneni dışında bir yerde görünüyorsa , derleme zamanı hatasıdır ( §15.15.4 ).21474836482147483648


12
Ve çarpma için JLS, Bir tamsayı çarpma taşması durumunda, sonuç, yeterince büyük ikinin tamamlayıcı biçiminde gösterildiği gibi matematiksel ürünün düşük dereceli bitleridir. Sonuç olarak, taşma meydana gelirse, sonucun işareti, iki işlenen değerinin matematiksel ürününün işareti ile aynı olmayabilir.
Chris K

3
Mükemmel cevap. Bazı insanlar taşmanın bir tür hata veya başarısızlık olduğu izlenimine sahip gibi görünüyor, ama değil.
Wouter Lievens

3
@ iowatiger08 Dil semantiği, JVM'den bağımsız olan JLS tarafından ana hatlarıyla belirtilmiştir (bu nedenle hangi JVM'yi kullandığınız önemli değildir).
arshajii

4
@WouterLievens, taşma olduğu normalde "olağandışı" koşulu, değilse salt hata durumu. Çoğu insanın matematik yaparken sezgisel olarak gerçekleşmesini beklemediği sonlu duyarlıklı matematiğin bir sonucudur. Bazı durumlarda, -1 + 1zararsız; ama 1024^4insanları görmeyi beklediklerinden tamamen beklenmedik sonuçlarla kör edebilir. Kullanıcı için en az bir uyarı veya not olmalı ve sessizce görmezden gelmemeliyim.
Phil Perry

1
iowatiger08: int boyutu sabittir; o mu değil JVM bağlıdır. Java C. değil
Martin Schröder

43

1024 * 1024 * 1024 * 1024ve 2147483648Java'da aynı değere sahip değildir.

Aslında, Java'da 2147483648 DEĞER DEĞİLDİR (olmasına rağmen 2147483648L). Derleyici tam olarak ne olduğunu veya nasıl kullanılacağını bilmiyor. Yani şaraplar.

1024Java'da geçerli bir int ve geçerli intbaşka bir geçerli ile çarpılır int, her zaman geçerlidir int. Sezgisel olarak beklediğiniz aynı değer olmasa bile, hesaplama taşacak.

Misal

Aşağıdaki kod örneğini düşünün:

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

Bunun bir derleme hatası oluşturmasını bekler misiniz? Şimdi biraz daha kayganlaşıyor.
Ya 3 yinelemeli bir döngü koyar ve döngüde çoğalırsak?

Derleyicinin optimizasyonuna izin verilir, ancak programın davranışını değiştiremez.


Bu davanın nasıl ele alındığına dair bazı bilgiler:

Java ve diğer birçok dilde, tamsayılar sabit sayıda bitten oluşur. Verilen bit sayısına uymayan hesaplamalar taşacaktır ; hesaplama temelde Java'da 2 ^ 32 modülüdür , bundan sonra değer işaretli bir tamsayıya dönüştürülür .

Diğer diller veya API'ler dinamik sayıda bit kullanır ( BigIntegerJava'da), bir istisna oluşturur veya değeri sayı değil gibi sihirli bir değere ayarlar.


8
Benim için, " 2147483648bir DEĞER DEĞİL DEĞİL (her ne kadar 2147483648L)" ifadesi, @arshajii'nin yapmaya çalıştığı noktayı gerçekten pekiştirdi.
kdbanman

Ah, üzgünüm, evet, o bendim. Cevabınızda kavram taşması / modüler aritmetik eksikti. Düzenlememi kabul etmiyorsanız geri alabileceğinizi unutmayın.
Maarten Bodewes

@owlstead Düzenlemeniz gerçekten doğrudur. Bunu dahil etmememin sebebi şuydu: nasıl 1024 * 1024 * 1024 * 1024ele alındığına bakılmaksızın, bunun yazı ile aynı şey olmadığını vurgulamak istedim 2147473648. Bir dilin potansiyel olarak onunla başa çıkmasının birçok yolu vardır (ve birkaçını listelediniz). Makul bir şekilde ayrılmış ve kullanışlı. Bu yüzden bırakacağım. Popüler bir soru hakkında yüksek bir cevabınız olduğunda birçok bilgi giderek daha fazla gerekli hale gelir.
Cruncher

16

İkinci varyantın neden hata ürettiğini bilmiyorum.

Önerdiğiniz davranış - yani, bir hesaplama bir tamsayıda saklanabilecek en büyük değerden daha büyük bir değer ürettiğinde - tanı mesajının üretilmesi bir özelliktir . Herhangi bir özelliği kullanabilmeniz için, özellik düşünülmeli, iyi bir fikir olarak düşünülmeli, tasarlanmalı, belirlenmeli, uygulanmalı, test edilmeli, belgelenmeli ve kullanıcılara gönderilmelidir.

Java için bu listedeki bir veya daha fazla şey olmadı ve bu nedenle özelliğiniz yok. Hangisini bilmiyorum; bir Java tasarımcısına sormanız gerekir.

C # için, tüm bu şeyler gerçekleşti - yaklaşık on dört yıl önce - ve böylece C #'daki ilgili program C # 1.0'dan beri bir hata üretti.


45
Bu yararlı bir şey eklemez. Java'da bıçak almayı önemsemesem de, OP sorusuna hiç cevap vermedi.
Seiyria

29
@Seiyria: Orijinal poster "neden olmasın?" soru - "dünya neden olması gerektiği gibi değil?" gerçek kodla ilgili kesin bir teknik soru değildir ve bu nedenle StackOverflow için kötü bir sorudur. Belirsiz ve teknik olmayan bir soruya doğru cevabın belirsiz ve teknik olmayan olması şaşırtıcı olmamalıdır. Orijinal posteri daha iyi bir soru sormaya teşvik ediyorum ve "neden olmasın?" sorular.
Eric Lippert

18
@Seiyria: Not ettiğim kabul edilen cevap, bu belirsiz ve teknik olmayan soruya da cevap vermiyor; soru "bu neden bir hata değil?" ve kabul edilen cevap "yasal olduğu için" dir. Bu sadece soruyu yeniden ifade ediyor ; "gökyüzü neden yeşil değil?" "çünkü mavi" sorusuna cevap vermiyor. Ama soru kötü bir soru olduğu için, cevaplayanı hiç suçlamıyorum; cevap kötü bir soruya son derece makul bir cevaptır.
Eric Lippert

13
Bay Eric, Gönderdiğim soru şu: "Neden int i = 1024 * 1024 * 1024 * 1024; tutulmada hata raporu olmadan?". ve arshajii'nin cevabı tam olarak ne olduğumdur (belki daha fazla). Bazen herhangi bir soruyu çok doğru bir şekilde ifade edemiyorum. Bence bu yüzden bazı insanlar Stackoverflow'da yayınlanan bazı soruları daha doğru değiştiriyorlar. Bence "yasal olduğu için" cevabını almak istersem, bu soruyu göndermeyeceğim. Bazı "düzenli sorular" göndermek için elimden geleni yapacağım, ancak lütfen benim gibi bir öğrenciyi ve çok profesyonel olmayan birini anlayın. Teşekkürler.
WUJ

5
@WUJ Bu cevap IMHO ek içgörü ve bakış açısı sağlar. Tüm cevapları okuduktan sonra bu cevabı, verilen diğer cevaplar kadar geçerli olacak şekilde buldum. Ayrıca, geliştiricilerin bazı yazılım ürünlerinin tek uygulayıcıları olmadığı bilincini arttırır.
SoftwareCarpenter

12

Arshajii'nin cevabına ek olarak bir şey daha göstermek istiyorum:

Hataya neden olan ödev değil, basitçe literal kullanımıdır . Denediğinde

long i = 2147483648;

sağ taraf hala bir intliteral ve aralık dışında olduğundan bir derleme hatasına neden olduğunu fark edeceksiniz .

Bu nedenle, int-değerli işlemler (ve atamalar dahil) bir derleme hatası olmadan (ve bir çalışma zamanı hatası olmadan) taşabilir, ancak derleyici bu çok büyük değişmezleri işleyemez.


1
Sağ. Uzun bir int atamak örtük bir kadro içerir. Ancak değer, dökümün ilk gerçekleştiği yerde int olarak asla var olamaz :)
Cruncher

4

C: Çünkü bu bir hata değil.

Arka plan: Çarpma 1024 * 1024 * 1024 * 1024bir taşmaya neden olur. Taşma genellikle bir hatadır. Farklı programlama dilleri taşmalar meydana geldiğinde farklı davranışlar üretir. Örneğin, C ve C ++ imzalı tamsayılar için "tanımsız davranış" olarak adlandırır ve davranış imzasız tamsayı olarak tanımlanır (matematiksel sonucu alın, UINT_MAX + 1sonuç negatif olduğu sürece ekleyin , sonuç büyük UINT_MAX + 1olduğu sürece çıkarın UINT_MAX).

Java durumunda, intdeğerler içeren bir işlemin sonucu izin verilen aralıkta değilse, kavramsal olarak Java, sonuç izin verilen aralıkta olana kadar 2 ^ 32 ekler veya çıkarır. Dolayısıyla ifade tamamen yasaldır ve hatalı değildir. Sadece umduğunuz sonucu vermiyor.

Bu davranışın yardımcı olup olmadığını ve derleyicinin size bir uyarı vermesi gerekip gerekmediğini kesinlikle tartışabilirsiniz. Şahsen bir uyarının çok yararlı olacağını söyleyebilirim, ancak yasal Java olduğu için bir hata yanlış olur.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.