Perilaku aneh ketika Java mengkonversi int ke byte?


130
int i =132;

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

Mindboggling. Mengapa hasilnya -124?

Jawaban:


172

Di Jawa, an intadalah 32 bit. A byteadalah 8 bits.

Kebanyakan tipe primitif di Jawa ditandatangani, dan byte, short, int, dan longdikodekan dalam dua komplemen. ( charJenisnya tidak ditandatangani, dan konsep tanda tidak berlaku untuk boolean.)

Dalam skema nomor ini bit paling signifikan menentukan tanda nomor. Jika lebih banyak bit diperlukan, bit paling signifikan ("MSB") cukup disalin ke MSB baru.

Jadi jika Anda memiliki byte 255: 11111111 dan Anda ingin menyatakannya sebagai int(32 bit) Anda cukup menyalin 1 ke kiri 24 kali.

Sekarang, salah satu cara untuk membaca angka komplemen dua negatif adalah mulai dengan bit paling tidak signifikan, bergerak ke kiri sampai Anda menemukan 1 pertama, lalu balikkan setiap bit setelahnya. Angka yang dihasilkan adalah versi positif dari angka itu

Misalnya: 11111111pergi ke 00000001= -1. Inilah yang akan ditampilkan sebagai nilai Java.

Apa yang mungkin ingin Anda lakukan adalah mengetahui nilai byte yang tidak ditandatangani.

Anda dapat melakukannya dengan bitmask yang menghapus semuanya kecuali yang paling signifikan 8 bit. (0xff)

Begitu:

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

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

Akan dicetak: "Signed: -1 Unsigned: 255"

Apa yang sebenarnya terjadi di sini?

Kami menggunakan bitwise AND untuk menutupi semua bit tanda asing (angka 1 di sebelah kiri 8 bit paling tidak signifikan). Ketika sebuah int dikonversikan menjadi byte, Java memotong bit paling kiri 24 bit

1111111111111111111111111010101
&
0000000000000000000000001111111
=
0000000000000000000000001010101

Karena bit ke-32 sekarang adalah bit tanda dan bukan bit ke-8 (dan kami mengatur bit tanda ke 0 yang positif), 8 bit asli dari byte dibaca oleh Java sebagai nilai positif.


1
Bagus, penjelasan terbaik tentang hal ini, Wayne! Saya hanya mencari formalisasi matematika mengapa dalam representasi komplemen dua bit tanda dapat disalin di sebelah kanan untuk menambahkan bit. Sangat mudah untuk memahaminya memikirkan aturan bagaimana cara mendapatkan angka negatif dari suatu angka. yaitu: pertimbangkan semua bit dari kanan ke kiri dan tulis mereka tidak berubah sampai 1 pertama terdiri. Kemudian balikkan bit-bit berikutnya. Jika saya menganggap bit yang hilang sebagai 0s, mudah untuk memahami bahwa mereka semua pergi ke 1. Tapi saya mencari penjelasan yang lebih 'matematika'.
AgostinoX

Apa yang terjadi di sini signedByte & (0xff)adalah 0xffinterger literal, sehingga SignByte dipromosikan ke integer sebelum operasi bitwise dilakukan.
Kevin Wheeler

Itu bukan 0xFF, ini 0x7E dalam contoh Anda!
JohnyTex

89

132dalam digit ( basis 10 ) 1000_0100dalam bit ( basis 2 ) dan Java menyimpan intdalam 32 bit:

0000_0000_0000_0000_0000_0000_1000_0100

Algoritma untuk int-to-byte adalah truncate kiri; Algoritma for System.out.printlnadalah komplemen dua (komplemen dua adalah jika bit paling kiri adalah 1, tafsirkan sebagai komplemen satu negatif (invert bits) minus-satu.); Demikian System.out.println(int-to-byte( ))adalah:

  • interpret-as (if-leftmost-bit-is-1 [negatif (invert-bit (minus-one (] left-truncate ( 0000_0000_0000_0000_0000_0000_1000_0100) [)))])
  • = interpret-as (if-leftmost-bit-is-1 [negatif (invert-bit (minus-one (] 1000_0100[)))]))
  • = interpret-as (negative (invert-bits (minus-one ( 1000_0100))))
  • = interpret-as (negative (invert-bits ( 1000_0011)))
  • = interpret-as (negative ( 0111_1100))
  • = interpret-as (negative (124))
  • = interpret-as (-124)
  • = -124 Tada !!!

7
Dijelaskan dengan sangat baik
ZAJ

1
Jadi sekarang 132 dalam desimal adalah -124 dalam byte. Bagaimana cara kerja sebaliknya?
Nilesh Deokar

@NileshDeokar, kebalikannya adalah oleh POLA karena mereka cocok (; cf JLS 5.1.2 ); output bertepatan dengan sign-leftpad ( 0untuk positif dan 1negatif).
Pacerier

Apa itu POLA? Konversi dari intke byteadalah konversi yang hilang (mis. Informasi hilang). Karena itu, tidak ada cara untuk mengubahnya kembali menjadi intnilai aslinya .
truthadjustr

23

byte di Java ditandatangani, sehingga memiliki rentang -2 ^ 7 hingga 2 ^ 7-1 - yaitu, -128 hingga 127. Karena 132 berada di atas 127, Anda akhirnya membungkus sekitar 132-256 = -124. Artinya, pada dasarnya 256 (2 ^ 8) ditambahkan atau dikurangi sampai jatuh ke dalam jangkauan.

Untuk informasi lebih lanjut, Anda mungkin ingin membaca di komplemen dua .


16

132 berada di luar kisaran byte yang -128 hingga 127 (Byte.MIN_VALUE ke Byte.MAX_VALUE) Alih-alih bit teratas dari nilai 8-bit diperlakukan sebagai tanda yang mengindikasikan negatif dalam kasus ini. Jadi angkanya 132 - 256 = -124.


5

di sini adalah metode yang sangat mekanis tanpa teori-teori yang mengganggu:

  1. Ubah angka menjadi representasi biner (gunakan kalkulator ok?)
  2. Hanya salin 8 bit paling kanan (LSB) dan buang sisanya.
  3. Dari hasil langkah # 2, jika bit paling kiri adalah 0, maka gunakan kalkulator untuk mengonversi angka menjadi desimal. Ini jawaban kamu.
  4. Lain (jika bit paling kiri adalah 1) jawaban Anda negatif. Biarkan semua nol paling kanan dan bit non-nol pertama tidak berubah. Dan membalikkan sisanya, yaitu, ganti 1 dengan 0 dan 0 dengan 1. Kemudian gunakan kalkulator untuk mengonversi menjadi desimal dan menambahkan tanda negatif untuk menunjukkan nilainya negatif.

Metode yang lebih praktis ini sesuai dengan banyak jawaban teoretis di atas. Jadi, mereka yang masih membaca buku-buku Java mengatakan untuk menggunakan modulo, ini pasti salah karena 4 langkah yang saya jelaskan di atas jelas bukan operasi modulo.


Apa yang dikatakan buku-buku Java untuk menggunakan 'modulo'? Saya belum pernah melihat buku CS yang menyatakan bahwa dalam 46 tahun, apalagi buku Java. Apa itu 'modulo'? Tidak ada operasi modulo di Jawa. Hanya operator sisanya.
Marquis of Lorne

grep lebih keras. http://iiti.ac.in/people/~tanimad/JavaTheCompleteReference.pdfhalaman 59
truthadjustr

4

Persamaan komplemen dua:

masukkan deskripsi gambar di sini


Di Jawa, byte(N = 8) dan int(N = 32) diwakili oleh komplemen 2s yang ditunjukkan di atas.

Dari persamaan, 7 adalah negatif untuk bytetetapi positif untuk 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

sering dalam buku Anda akan menemukan penjelasan casting dari int ke byte yang dilakukan oleh divisi modulus. ini tidak sepenuhnya benar seperti yang ditunjukkan di bawah ini apa yang sebenarnya terjadi adalah 24 bit paling signifikan dari nilai biner dari nomor int dibuang meninggalkan kebingungan jika bit paling kiri yang tersisa diatur yang menunjuk angka sebagai negatif

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
Saya belum pernah melihat itu dalam buku apa pun dalam 46 tahun.
Marquis of Lorne

2

Algoritme cepat yang mensimulasikan cara kerjanya adalah sebagai berikut:

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

Bagaimana cara kerjanya? Lihat jawaban daixtr . Implementasi dari algoritma tepat yang dijelaskan dalam jawabannya adalah sebagai berikut:

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

Jika Anda ingin memahami ini secara matematis, seperti cara kerjanya

jadi pada dasarnya angka b / w -128 hingga 127 akan ditulis sama dengan nilai desimalnya, di atas angka itu (nomor Anda - 256).

misalnya. 132, jawabannya adalah 132 - 256 = - 124 yaitu

256 + jawaban Anda di nomor 256 + (-124) adalah 132

Contoh lain

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

Output akan menjadi 39 44

(295 - 256) (300 - 256)

CATATAN: tidak akan mempertimbangkan angka setelah desimal.


0

Secara konseptual, pengurangan berulang 256 dilakukan ke nomor Anda, sampai berada di kisaran -128 hingga +127. Jadi dalam kasus Anda, Anda mulai dengan 132, lalu berakhir dengan -124 dalam satu langkah.

Secara komputasional, ini sesuai dengan mengekstraksi 8 bit paling tidak signifikan dari nomor asli Anda. (Dan perhatikan bahwa bit yang paling signifikan dari 8 ini menjadi bit tanda.)

Perhatikan bahwa dalam bahasa lain perilaku ini tidak didefinisikan (misalnya C dan C ++).


Agar jelas, hasil yang Anda dapatkan sama dengan jika pengurangan berulang dilakukan. Dalam praktiknya, JVM sebenarnya tidak melakukannya dengan cara ini. (Itu akan sangat tidak efisien!)
Stephen C

Memang. Saya harap paragraf kedua saya mencakup bagaimana JVM sebenarnya melakukan ini. Tapi saya sedikit mengotak-atik bahasa saya.
Batsyeba

1
Iya. Perubahan "dasarnya" menjadi "secara konseptual" membuat perbedaan besar!
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           

Jawaban yang benar didapat dengan bit-masking, bukan dengan pembagian dan sisanya.
Marquis of Lorne
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.