Cara Mengonversi Int ke Unsigned Byte dan Back

93

Saya perlu mengubah angka menjadi byte yang tidak ditandatangani. Angka tersebut selalu kurang dari atau sama dengan 255, sehingga akan muat dalam satu byte.

Saya juga perlu mengubah byte itu kembali menjadi angka itu. Bagaimana saya melakukannya di Jawa? Saya sudah mencoba beberapa cara dan tidak ada yang berhasil. Inilah yang saya coba lakukan sekarang:

int size = 5;
// Convert size int to binary
String sizeStr = Integer.toString(size);
byte binaryByte = Byte.valueOf(sizeStr);

dan sekarang untuk mengubah byte itu kembali menjadi angka:

Byte test = new Byte(binaryByte);
int msgSize = test.intValue();

Jelas, ini tidak berhasil. Untuk beberapa alasan, selalu mengubah angka menjadi 65. Ada saran?

Langit yang gelap
sumber
Saya juga mencoba metode ini: crazysquirrel.com/computing/java/basics/… - tidak berhasil.
darksky

Jawaban:

205

Sebuah byte selalu ditandatangani di Java. Anda bisa mendapatkan nilai unsigned dengan binary-anding dengan 0xFF, meskipun:

int i = 234;
byte b = (byte) i;
System.out.println(b); // -22
int i2 = b & 0xFF;
System.out.println(i2); // 234
JB Nizet
sumber
Saya masih berusia 65 ... Mengapa demikian?
darksky
1
Ini menyimpan nomor yang Anda inginkan dalam byte. Java hanya menganggapnya sebagai nomor bertanda tangan daripada nomor tak bertanda tangan. Tetapi setiap bit sama seperti jika itu adalah nomor yang tidak bertanda tangan. Konversi string Anda menjadi byte adalah pertanyaan lain yang tidak terkait. Posting secara terpisah, di pertanyaan baru.
JB Nizet
1
gunakan getInt untuk mendapatkan kembali int Anda, dan ubah int sebagai byte. Int adalah int. Dari mana asalnya tidak masalah.
JB Nizet
19
Mengapa bitwise dan dengan 0xFFhasil berupa unsigned integer? Beberapa penjelasan akan sangat membantu.
pengguna462455
2
Java 8 menggunakan metode yang sama: public static int toUnsignedInt (byte x) {return ((int) x) & 0xff; }
Daniel De León
57

Java 8 menyediakan Byte.toUnsignedIntuntuk dikonversi byteke intdengan konversi tidak bertanda tangan. Dalam JDK Oracle, ini hanya diimplementasikan return ((int) x) & 0xff;karena HotSpot sudah memahami cara mengoptimalkan pola ini, tetapi pola ini dapat diintrinsifikasi pada VM lain. Lebih penting lagi, tidak diperlukan pengetahuan sebelumnya untuk memahami apa yang dilakukan oleh sebuah panggilan toUnsignedInt(foo).

Secara total, Java 8 menyediakan metode untuk mengkonversi bytedan shortmenjadi unsigned intdan long, dan intmenjadi unsignedlong . Sebuah metode untuk mengkonversi byteke unsigned shortitu sengaja dihilangkan karena JVM hanya menyediakan aritmatika pada intdan longtetap.

Untuk mengkonversi int kembali ke byte, hanya menggunakan gips: (byte)someInt. Konversi primitif penyempitan yang dihasilkan akan membuang semua kecuali 8 bit terakhir.

Jeffrey Bosboom
sumber
7

Jika Anda hanya perlu mengonversi nilai 8-bit yang diharapkan dari int yang ditandatangani menjadi nilai yang tidak ditandatangani, Anda dapat menggunakan pergeseran bit sederhana:

int signed = -119;  // 11111111 11111111 11111111 10001001

/**
 * Use unsigned right shift operator to drop unset bits in positions 8-31
 */
int psuedoUnsigned = (signed << 24) >>> 24;  // 00000000 00000000 00000000 10001001 -> 137 base 10

/** 
 * Convert back to signed by using the sign-extension properties of the right shift operator
 */
int backToSigned = (psuedoUnsigned << 24) >> 24; // back to original bit pattern

http://docs.oracle.com/javase/tutorial/java/nutsandbolts/op3.html

Jika menggunakan sesuatu selain intsebagai tipe dasar, Anda jelas perlu menyesuaikan jumlah shift: http://docs.oracle.com/javase/tutorial/java/nutsandbolts/datatypes.html

Juga, perlu diingat bahwa Anda tidak dapat menggunakan bytetipe, hal itu akan menghasilkan nilai yang ditandatangani seperti yang disebutkan oleh penjawab lain. Jenis primitif terkecil yang dapat Anda gunakan untuk merepresentasikan nilai 8-bit unsigned adalah a short.

dm78
sumber
3

The Integer.toString(size)panggilan bertobat ke dalam representasi char bilangan bulat Anda, yaitu char '5'. The ASCII representasi dari karakter yang nilai 65.

Anda perlu mengurai string kembali ke nilai integer terlebih dahulu, misalnya dengan menggunakan Integer.parseInt , untuk mendapatkan kembali nilai int asli.

Intinya, untuk konversi yang ditandatangani / tidak bertanda tangan, yang terbaik adalah meninggalkan Stringgambar dan menggunakan manipulasi bit seperti yang disarankan @JB.

Péter Török
sumber
Jadi seperti ini? (Masih mendapatkan 65 di sini)String sizeStr = Integer.toString(size); int sizeInt = Integer.parseInt(sizeStr); byte binaryByte = Byte.valueOf((byte) sizeInt);
darksky
@Nayefc, saya memperbarui jawaban saya tepat ketika Anda menulis komentar Anda :-)
Péter Török
Baik terima kasih! Untuk beberapa alasan itu MASIH mencetak 65 meskipun. Apa artinya ini? Jika hanya menunjukkan 65, apa yang sebenarnya disimpan dalam byte maka saya tidak mengerti. Juga silakan periksa pertanyaan saya, saya mengeditnya dan menambahkan bagian tentang konversi string :)
darksky
@Nayefc, sekali lagi, String.getBytes()menghasilkan nilai ASCII dari karakter yang terdapat dalam teks, bukan nilai numerik dari digit. Anda perlu mengurai string menjadi nilai numerik, seperti yang saya tunjukkan di atas.
Péter Török
1
@Nayefc, jadi itu sebenarnya pertanyaan yang sama sekali berbeda. Dan AFAIS seharusnya berhasil. Buat pertanyaan baru, termasuk masukan dan keluaran aktual dari konversi, dan kode lengkap untuk mereproduksi kasus.
Péter Török
3

Solusinya bekerja dengan baik (terima kasih!), Tetapi jika Anda ingin menghindari casting dan membiarkan pekerjaan level rendah ke JDK, Anda dapat menggunakan DataOutputStream untuk menulis int dan DataInputStream untuk membacanya kembali. Mereka secara otomatis diperlakukan sebagai unsigned byte kemudian:

Untuk mengubah int menjadi byte biner;

ByteArrayOutputStream bos = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(bos);
int val = 250;
dos.write(byteVal);
...
dos.flush();

Membacanya kembali dalam:

// important to use a (non-Unicode!) encoding like US_ASCII or ISO-8859-1,
// i.e., one that uses one byte per character
ByteArrayInputStream bis = new ByteArrayInputStream(
   bos.toString("ISO-8859-1").getBytes("ISO-8859-1"));
DataInputStream dis = new DataInputStream(bis);
int byteVal = dis.readUnsignedByte();

Esp. berguna untuk menangani format data biner (mis. format pesan datar, dll.)

Gregor
sumber
3

Kecuali char, setiap tipe data numerik lainnya di Java ditandatangani.

Seperti yang dikatakan dalam jawaban sebelumnya, Anda bisa mendapatkan nilai unsigned dengan melakukan andoperasi dengan 0xFF. Dalam jawaban ini, saya akan menjelaskan bagaimana itu terjadi.

int i = 234;
byte b = (byte) i;
System.out.println(b);  // -22

int i2 = b & 0xFF;      
// This is like casting b to int and perform and operation with 0xFF

System.out.println(i2); // 234

Jika mesin Anda 32-bit, maka inttipe datanya membutuhkan 32-bit untuk menyimpan nilai. bytehanya membutuhkan 8-bit.

The intvariabel idirepresentasikan dalam memori sebagai berikut (sebagai integer 32-bit).

0{24}11101010

Kemudian bytevariabel bdirepresentasikan sebagai:

11101010

Karena bytetidak bertanda tangan, nilai ini mewakili-22 . (Cari komplemen 2 untuk mempelajari lebih lanjut tentang bagaimana merepresentasikan bilangan bulat negatif dalam memori)

Kemudian jika Anda mentransmisikannya ke intsana, itu tetap -22karena pengecoran mempertahankan tanda sebuah angka.

1{24}11101010

Nilai yang dicor 32-bitdari bmelakukan andoperasi dengan 0xFF.

 1{24}11101010 & 0{24}11111111
=0{24}11101010

Kemudian Anda mendapatkan 234jawabannya.

Ramesh-X
sumber
1

Meskipun sudah terlambat, saya ingin memberikan masukan tentang hal ini karena mungkin dapat menjelaskan mengapa solusi yang diberikan oleh JB Nizet berhasil. Saya menemukan masalah kecil ini bekerja pada pengurai byte dan konversi string sendiri. Ketika Anda menyalin dari tipe integral berukuran lebih besar ke tipe integral berukuran lebih kecil seperti yang dikatakan dokumen java ini:

https://docs.oracle.com/javase/specs/jls/se7/html/jls-5.html#jls-5.1.3 Konversi yang dipersempit dari bilangan bulat bertanda ke tipe integral T hanya membuang semua kecuali n terendah order bits, di mana n adalah jumlah bit yang digunakan untuk merepresentasikan tipe T. Selain kemungkinan hilangnya informasi tentang besarnya nilai numerik, hal ini dapat menyebabkan tanda nilai yang dihasilkan berbeda dari tanda nilai masukan. .

Anda dapat yakin bahwa byte adalah tipe integral seperti yang dikatakan dokumen java ini https://docs.oracle.com/javase/tutorial/java/nutsandbolts/datatypes.html byte: Tipe data byte adalah 8-bit bertanda dua melengkapi integer.

Jadi dalam kasus casting integer (32 bit) menjadi byte (8 bit), Anda cukup menyalin yang terakhir (8 bit paling tidak signifikan) dari integer itu ke variabel byte yang diberikan.

int a = 128;
byte b = (byte)a; // Last 8 bits gets copied
System.out.println(b);  // -128

Bagian kedua dari cerita ini melibatkan bagaimana Java unary dan operator biner mempromosikan operan. https://docs.oracle.com/javase/specs/jls/se7/html/jls-5.html#jls-5.6.2 Konversi primitif pelebaran (§5.1.2) diterapkan untuk mengonversi salah satu atau kedua operan seperti yang ditentukan dengan aturan berikut:

Jika salah satu operan berjenis double, operan lainnya akan diubah menjadi double.

Sebaliknya, jika salah satu operand berjenis float, yang lain akan diubah menjadi float.

Sebaliknya, jika salah satu operan berjenis long, operan lainnya akan diubah menjadi long.

Jika tidak, kedua operan akan diubah menjadi tipe int.

Yakinlah, jika Anda bekerja dengan tipe integral int dan / atau lebih rendah itu akan dipromosikan menjadi int.

// byte b(0x80) gets promoted to int (0xFF80) by the & operator and then
// 0xFF80 & 0xFF (0xFF translates to 0x00FF) bitwise operation yields 
// 0x0080
a = b & 0xFF;
System.out.println(a); // 128

Saya menggaruk-garuk kepala saya di sekitar ini juga :). Ada jawaban bagus untuk ini di sini oleh rgettman. Operator bitwise di java hanya untuk integer dan long?

KaziQ
sumber
0

Jika Anda ingin menggunakan kelas pembungkus primitif, ini akan berhasil, tetapi semua jenis java ditandatangani secara default.

public static void main(String[] args) {
    Integer i=5;
    Byte b = Byte.valueOf(i+""); //converts i to String and calls Byte.valueOf()
    System.out.println(b);
    System.out.println(Integer.valueOf(b));
}
Romeo
sumber
0

Menangani byte dan integer yang tidak ditandatangani dengan BigInteger:

byte[] b = ...                    // your integer in big-endian
BigInteger ui = new BigInteger(b) // let BigInteger do the work
int i = ui.intValue()             // unsigned value assigned to i
pgrandjean.dll
sumber
Ini mengkonversi ke integer SIGNED, tidak unsigned ...
Pedro
-1

di java 7

public class Main {
    public static void main(String[] args) {
        byte b =  -2;
        int i = 0 ;
        i = ( b & 0b1111_1111 ) ;
        System.err.println(i);
    }
}

hasil: 254

Tristan De Smet
sumber