Java int'i bayta dönüştürdüğünde garip davranış?


130
int i =132;

byte b =(byte)i; System.out.println(b);

Mindboggling. Çıktı neden -124?

Yanıtlar:


172

Java'da bir int32 bittir. A byte, 8'dir bits.

Java En ilkel tipleri imzaladı ve edilir byte, short, intve longiki bilgisayarın tamamlayıcı kodlanmıştır. ( charTip işaretsizdir ve işaret kavramı için geçerli değildir boolean.)

Bu sayı şemasında en önemli bit, sayının işaretini belirtir. Daha fazla bit gerekliyse, en anlamlı bit ("MSB") basitçe yeni MSB'ye kopyalanır.

Dolayısıyla, baytınız varsa 255: 11111111 ve bunu bir int(32 bit) olarak göstermek istiyorsanız, 1'i 24 kez sola kopyalarsınız.

Şimdi, negatif ikinin tümleyen sayısını okumanın bir yolu, en önemsiz bit ile başlamak, ilk 1'i bulana kadar sola hareket etmek, ardından her biti tersine çevirmektir. Ortaya çıkan sayı, bu sayının pozitif sürümüdür

Örneğin: = konumuna 11111111gider . Java'nın değer olarak göstereceği şey budur.00000001-1

Muhtemelen yapmak isteyeceğiniz şey, baytın işaretsiz değerini bilmektir.

Bunu, en az önemli olan 8 bit hariç her şeyi silen bir bit maskesi ile başarabilirsiniz. (0xff)

Yani:

byte signedByte = -1;
int unsignedByte = signedByte & (0xff);

System.out.println("Signed: " + signedByte + " Unsigned: " + unsignedByte);

Yazdırılacak: "Signed: -1 Unsigned: 255"

Burada gerçekte neler oluyor?

Tüm yabancı işaret bitlerini maskelemek için bitsel AND kullanıyoruz (1'ler en az önemli olan 8 bitin solundadır.) Bir int bayta dönüştürüldüğünde, Java en soldaki 24 biti keser.

1111111111111111111111111010101
&
0000000000000000000000001111111
=
0000000000000000000000001010101

32. bit artık 8. bit yerine işaret biti olduğundan (ve işaret bitini pozitif olan 0'a ayarladık), bayttan gelen orijinal 8 bit, Java tarafından pozitif bir değer olarak okunur.


1
aferin, bu konudaki en iyi açıklama, Wayne! Sadece bir ikinin tümleyen gösteriminde bit eklemek için işaret bitinin neden sağda kopyalanabileceği matematiksel biçimlendirmeyi arıyorum. Bir sayının negatifinin nasıl elde edileceğini düşünerek onu anlamak kolaydır. yani: sağdan sola tüm bitleri düşünün ve ilk 1'i oluşana kadar değiştirmeden yazın. Ardından sonraki bitleri ters çevirin. Eksik bitin 0 olduğunu düşünürsem, hepsinin 1'e gittiğini anlamak kolaydır. Ama daha 'matematik' bir açıklama arıyordum.
AgostinoX

Burada signedByte & (0xff)yaşanan 0xffşey , bunun bir interger literal olmasıdır, bu nedenle işaretliByte, bitsel işlem gerçekleştirilmeden önce bir tam sayıya yükseltilir.
Kevin Wheeler

Bu 0xFF değil, örneğinizde 0x7E var!
JohnyTex

89

132rakamlar ( 10 tabanı ) 1000_0100bit ( 2 taban ) cinsindendir ve Java int32 bit olarak depolar :

0000_0000_0000_0000_0000_0000_1000_0100

Bayt arası algoritma soldan kesiktir; Algoritma System.out.printlnis ikinin-tamamlayıcısıdır (İkinin-tamamlayıcısı, en soldaki bit ise 1, negatif bir tümleyen (bitleri ters çevir) eksi-bir olarak yorumlanır.); Böylece System.out.println(int-to-byte( )):

  • yorum-olarak (eğer-en soldaki-bit-1 ise [negatif (ters-bitler (eksi-bir (] sol-kes ( 0000_0000_0000_0000_0000_0000_1000_0100) [)))])
  • = yorum-olarak (eğer-en soldaki-bit-1 ise [negatif (ters-bitler (eksi-bir (] 1000_0100[)))])
  • = yorum-olarak (negatif (ters-bitler (eksi-bir ( 1000_0100))))
  • = yorum-olarak (negatif (ters-bitler ( 1000_0011)))
  • = yorum-olarak (negatif ( 0111_1100))
  • = Yorumlamak-olarak (negatif (124))
  • = Yorumlamak-olarak (-124)
  • = -124 Tada !!!

7
Çok güzel bir şekilde açıkladı
ZAJ

1
Şimdi ondalık sayıdaki 132 bayt olarak -124'tür. Tersi nasıl çalışır?
Nilesh Deokar

@NileshDeokar, Tam tersi POLA'ya göre uygun olduklarından (; cf JLS 5.1.2 ); çıktı işaret-sol pedi ile çakışır ( 0pozitif ve 1negatif için).
Pacerier

POLA nedir? Dan dönüşüm inta bytekayıplı dönüşüm (yani bilgileri kaybolur). Bu nedenle, onu orijinal intdeğerine geri dönüştürmenin bir yolu yoktur .
truthadjustr

23

Java'da bayt imzalıdır, bu nedenle -2 ^ 7 ila 2 ^ 7-1 - yani -128 ila 127 aralığına sahiptir. 132 127'nin üzerinde olduğundan, 132-256 = -124'e ulaşırsınız. Yani, aralığa düşene kadar esasen 256 (2 ^ 8) eklenir veya çıkarılır.

Daha fazla bilgi için, ikinin tümleyenlerini okumak isteyebilirsiniz .


16

132, -128 ila 127 olan bir bayt aralığının dışındadır (Byte.MIN_VALUE ila Byte.MAX_VALUE) Bunun yerine, 8 bitlik değerin üst biti, bu durumda negatif olduğunu gösteren işaretli olarak değerlendirilir. Yani sayı 132-256 = -124'tür.


5

dikkat dağıtıcı teorilerin olmadığı çok mekanik bir yöntem:

  1. Sayıyı ikili gösterime dönüştürün (bir hesap makinesi kullanın tamam mı?)
  2. Yalnızca en sağdaki 8 biti (LSB) kopyalayın ve geri kalanını atın.
  3. 2. adımın sonucundan, en soldaki bit 0 ise, sayıyı ondalık sayıya dönüştürmek için bir hesap makinesi kullanın. Cevabınız bu.
  4. Aksi takdirde (en soldaki bit 1 ise) cevabınız olumsuzdur. En sağdaki tüm sıfırları ve sıfır olmayan ilk biti değiştirmeden bırakın. Ve geri kalanı tersine çevirdi, yani 1'leri 0'larla ve 0'ları 1'lerle değiştirdi. Ardından, ondalık sayıya dönüştürmek için bir hesap makinesi kullanın ve değerin negatif olduğunu belirtmek için bir eksi işareti ekleyin.

Bu daha pratik yöntem, yukarıdaki teorik cevapların çoğuna uygundur. Yani, hala o Java kitaplarını okuyanlar modulo kullan diyorlar, bu kesinlikle yanlış çünkü yukarıda özetlediğim 4 adım kesinlikle bir modulo işlemi değil.


'Modulo' kullanmak için Java kitapları ne diyor? Bırakın herhangi bir Java kitabı, 46 yıldır bunu belirten bir bilgisayar bilimi kitabı görmedim. Ne 'modulo'? Java'da modulo işlemi yoktur. Yalnızca kalan operatör.
Marquis of Lorne

grep harder. http://iiti.ac.in/people/~tanimad/JavaTheCompleteReference.pdfsayfa 59
truthadjustr

4

İkinin Tümleyen Denklemi:

görüntü açıklamasını buraya girin


Java'da, byte(N = 8) ve int(N = 32), yukarıda gösterilen 2s-tamamlayıcısı ile temsil edilir.

Denklemden, 7 için negatif byteama pozitiftir int.

coef:   a7    a6  a5  a4  a3  a2  a1  a0
Binary: 1     0   0   0   0   1   0   0
----------------------------------------------
int:    128 + 0 + 0 + 0 + 0 + 4 + 0 + 0 =  132
byte:  -128 + 0 + 0 + 0 + 0 + 4 + 0 + 0 = -124

2

genellikle kitaplarda modül bölme ile gerçekleştirilen int'ten bayta dönüştürme açıklamasını bulacaksınız. Bu, aşağıda gösterildiği gibi tam olarak doğru değildir, int sayısının ikili değerinden en önemli 24 bit, kalan en soldaki bit sayıyı negatif olarak belirleyen ayarlanırsa kafa karışıklığı bırakarak atılır.

public class castingsample{

public static void main(String args[]){

    int i;
    byte y;
    i = 1024;
    for(i = 1024; i > 0; i-- ){

      y = (byte)i;
      System.out.print(i + " mod 128 = " + i%128 + " also ");
      System.out.println(i + " cast to byte " + " = " + y);

    }

}

}

2
46 yıldır bunu hiçbir kitapta görmedim.
Lorne Markisi

2

Çalışma şeklini simüle eden hızlı bir algoritma şu şekildedir:

public int toByte(int number) {
    int tmp = number & 0xff
    return (tmp & 0x80) == 0 ? tmp : tmp - 256;
}

Bu nasıl çalışır? Bak daixtr cevap. Cevabında açıklanan kesin algoritmanın bir uygulaması şu şekildedir:

public static int toByte(int number) {
    int tmp = number & 0xff;
    if ((tmp & 0x80) == 0x80) {
        int bit = 1;
        int mask = 0;
        for(;;) {
            mask |= bit;
            if ((tmp & bit) == 0) {
                bit <<=1;
                continue;
            }
            int left = tmp & (~mask);
            int right = tmp & mask;
            left = ~left;
            left &= (~mask);
            tmp = left | right;
            tmp = -(tmp & 0xff);
            break;
        }
    }
    return tmp;
}

1

Bunu matematiksel olarak anlamak istiyorsanız, bunun nasıl çalıştığını

yani temelde b / w -128'den 127'ye kadar olan sayılar ondalık değerleriyle aynı yazılır, bunun üstüne (sizin sayınız - 256).

Örneğin. 132, cevap 132 - 256 = - 124 yani

256 + 256 + (-124) numaralı cevabınız 132

Başka bir örnek

double a = 295.04;
int b = 300;
byte c = (byte) a;
byte d = (byte) b; System.out.println(c + " " + d);

Çıktı 39 44 olacak

(295-256) (300-256)

NOT: Ondalıktan sonraki sayıları dikkate almaz.


0

Kavramsal olarak, sayınıza -128 ila +127 aralığına gelene kadar 256'dan tekrarlı çıkarma yapılır. Yani sizin durumunuzda, 132 ile başlarsınız, sonra tek adımda -124 ile sonuçlanırsınız.

Hesaplama açısından bu, orijinal numaranızdan en az önemli 8 bitin çıkarılmasına karşılık gelir. (Ve bu 8'in en önemli bitinin işaret biti olduğuna dikkat edin.)

Diğer dillerde bu davranışın tanımlanmadığını unutmayın (örneğin C ve C ++).


Açık olmak gerekirse, sen olsun sonuç aynıdır sanki tekrarlandı çıkarmalar yapıldı. Uygulamada, JVM bunu aslında bu şekilde yapmaz. (Korkunç derecede verimsiz olurdu!)
Stephen C

Aslında. Umarım ikinci paragrafım JVM'nin bunu gerçekte nasıl yaptığını kapsar. Ama biraz dilimi karıştırdım.
Bathsheba

1
Evet. "Esasen" ifadesinin "kavramsal olarak" değişmesi büyük bir fark yaratır!
Stephen C

-1
 N is input number
case 1: 0<=N<=127  answer=N;
case 2: 128<=N<=256 answer=N-256 
case 3: N>256   
        temp1=N/256;
        temp2=N-temp*256;
        if temp2<=127   then answer=temp2;
        else if temp2>=128  then answer=temp2-256;
case 4: negative  number input
        do same procedure.just change the sign of the solution           

Doğru cevaba bölüm ve kalanla değil, bit maskeleme ile ulaşılır.
Marquis of Lorne
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.