Hành vi kỳ quặc khi Java chuyển đổi int thành byte?


130
int i =132;

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

Tâm boggling. Tại sao là đầu ra -124?

Câu trả lời:


172

Trong Java, một intlà 32 bit. A bytelà 8 bits.

Hầu hết các loại nguyên thủy trong Java được ký kết, và byte, short, int, và longđược mã hóa trong bổ sung hai nhân. ( charLoại không dấu và khái niệm về một dấu hiệu không được áp dụng cho boolean.)

Trong sơ đồ số này, bit quan trọng nhất chỉ định dấu của số. Nếu cần nhiều bit hơn, bit quan trọng nhất ("MSB") chỉ đơn giản được sao chép sang MSB mới.

Vì vậy, nếu bạn có byte 255: 11111111 và bạn muốn biểu diễn nó dưới dạng int(32 bit), bạn chỉ cần sao chép 1 sang trái 24 lần.

Bây giờ, một cách để đọc số bổ sung của hai số âm là bắt đầu với bit có trọng số thấp nhất, di chuyển sang trái cho đến khi bạn tìm thấy số 1 đầu tiên, sau đó đảo ngược từng bit sau đó. Số kết quả là phiên bản dương của số đó

Ví dụ: 11111111đi đến 00000001= -1. Đây là những gì Java sẽ hiển thị như là giá trị.

Những gì bạn có thể muốn làm là biết giá trị không dấu của byte.

Bạn có thể thực hiện điều này với một bitmask xóa mọi thứ trừ 8 bit quan trọng nhất. (0xff)

Vì thế:

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

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

Sẽ in ra: "Signed: -1 Unsigned: 255"

Điều gì thực sự xảy ra ở đây?

Chúng tôi đang sử dụng bitwise AND để che dấu tất cả các bit ký hiệu không liên quan (1 bit ở bên trái của 8 bit có ý nghĩa nhỏ nhất.) Khi một int được chuyển đổi thành một byte, Java tách ra 24 bit bên trái

1111111111111111111111111010101
&
0000000000000000000000001111111
=
0000000000000000000000001010101

Do bit thứ 32 bây giờ là bit dấu thay vì bit thứ 8 (và chúng tôi đặt bit dấu thành 0 là dương), 8 bit gốc từ byte được Java đọc làm giá trị dương.


1
làm tốt lắm, lời giải thích tốt nhất về chủ đề này, Wayne! Tôi chỉ tìm kiếm sự chính thức hóa toán học tại sao trong biểu diễn bổ sung của hai, bit dấu có thể được sao chép ở bên phải để thêm bit. Thật dễ hiểu khi nó suy nghĩ theo quy tắc làm thế nào để có được số âm của một số. đó là: xem xét tất cả các bit từ phải sang trái và viết chúng không thay đổi cho đến 1 đầu tiên bao gồm. Sau đó đảo ngược các bit tiếp theo. Nếu tôi coi bit bị thiếu là 0, thật dễ hiểu khi tất cả đều chuyển sang 1. Nhưng tôi đang tìm kiếm một lời giải thích 'toán học' hơn.
AgostinoX

Điều đáng mừng ở đây signedByte & (0xff)0xffmột nghĩa đen, do đó SignByte được thăng cấp thành một số nguyên trước khi thao tác bitwise được thực hiện.
Kevin Wheeler

Đó không phải là 0xFF, đó là 0x7E trong ví dụ của bạn!
JohnyTex

89

132tính bằng chữ số ( cơ sở 10 ) tính 1000_0100bằng bit ( cơ sở 2 ) và Java lưu trữ inttrong 32 bit:

0000_0000_0000_0000_0000_0000_1000_0100

Thuật toán cho int-to-byte là cắt ngắn; Thuật toán cho System.out.printlnhai phần bù (Phần bù hai là nếu bit ngoài cùng bên trái là 1, diễn giải dưới dạng phần bù âm (phần tử đảo ngược) âm trừ một phần một.); Như vậy System.out.println(int-to-byte( ))là:

  • phiên dịch-as (if-left-bit-is-1 [âm (invert-bit (minus-one (] left-truncate ( 0000_0000_0000_0000_0000_0000_1000_0100) [)))])
  • = phiên dịch-as (if-left ultra-bit-is-1 [neg (invert-bits (minus-one (] 1000_0100[)))])
  • = phiên dịch-as (âm (invert-bits (trừ đi một ( 1000_0100))))
  • = phiên dịch-as (phủ định (invert-bits ( 1000_0011)))
  • = phiên dịch-as (phủ định ( 0111_1100))
  • = phiên dịch-as (phủ định (124))
  • = phiên dịch-như (-124)
  • = -124 Tada !!!

7
Giải thích rất độc đáo
ZAJ

1
Vì vậy, bây giờ 132 trong số thập phân là -124 theo byte. Làm thế nào để đảo ngược hoạt động?
Nilesh Deokar

@NileshDeokar, Điều ngược lại là do Pola vì chúng phù hợp (; cf JLS 5.1.2 ); đầu ra trùng với dấu hiệu bên trái ( 0cho dương và 1âm).
Pacerier

Pola là gì? Sự hội tụ từ intđến a bytelà một sự hội tụ mất mát (tức là thông tin bị mất). Do đó, không có cách nào để chuyển đổi nó trở lại intgiá trị ban đầu của nó .
Truthadjustr

23

byte trong Java được ký, do đó, nó có phạm vi -2 ^ 7 đến 2 ^ 7-1 - tức là, -128 đến 127. Vì 132 ở trên 127, nên cuối cùng bạn sẽ bao quanh khoảng 132-256 = -124. Đó là, về cơ bản, 256 (2 ^ 8) được thêm hoặc trừ cho đến khi nó rơi vào phạm vi.

Để biết thêm thông tin, bạn có thể muốn đọc lên phần bổ sung của hai .


16

132 nằm ngoài phạm vi của một byte là -128 đến 127 (Byte.MIN_VALUE thành Byte.MAX_VALUE) Thay vào đó, bit trên cùng của giá trị 8 bit được coi là ký hiệu cho thấy nó âm trong trường hợp này. Vậy số đó là 132 - 256 = -124.


5

đây là một phương pháp rất cơ học mà không có lý thuyết gây mất tập trung:

  1. Chuyển đổi số thành biểu diễn nhị phân (sử dụng máy tính ok?)
  2. Chỉ sao chép 8 bit ngoài cùng bên phải (LSB) và loại bỏ phần còn lại.
  3. Từ kết quả của bước # 2, nếu bit ngoài cùng bên trái là 0, thì hãy sử dụng máy tính để chuyển đổi số thành số thập phân. Đây là câu trả lời của bạn.
  4. Khác (nếu bit ngoài cùng bên trái là 1) câu trả lời của bạn là âm. Để lại tất cả các số 0 ngoài cùng bên phải và bit khác không đầu tiên không thay đổi. Và đảo ngược phần còn lại, nghĩa là thay 1 bằng 0 và 0 bằng 1. Sau đó sử dụng máy tính để chuyển đổi thành số thập phân và nối thêm dấu âm để biểu thị giá trị âm.

Phương pháp thực tế hơn này phù hợp với nhiều câu trả lời lý thuyết ở trên. Vì vậy, những người vẫn đọc những cuốn sách Java nói rằng sử dụng modulo, điều này hoàn toàn sai vì 4 bước tôi đã nêu ở trên chắc chắn không phải là một hoạt động modulo.


Những cuốn sách Java nói gì để sử dụng 'modulo'? Tôi chưa bao giờ thấy bất kỳ cuốn sách CS nào nói rằng trong 46 năm qua, hãy để một mình bất kỳ cuốn sách Java nào. 'Modulo' là gì? Không có hoạt động modulo trong Java. Chỉ có một toán tử còn lại.
Hầu tước Lorne

grep khó hơn. http://iiti.ac.in/people/~tanimad/JavaTheCompleteReference.pdftrang 59
Truthadjustr

4

Phương trình bổ sung của hai:

nhập mô tả hình ảnh ở đây


Trong Java, byte(N = 8) và int(N = 32) được biểu thị bằng phần bù 2s được hiển thị ở trên.

Từ phương trình, 7 là âm cho bytenhưng dương cho 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

thông thường trong các cuốn sách, bạn sẽ tìm thấy lời giải thích về việc truyền từ int sang byte như được thực hiện bởi phép chia mô đun. điều này không hoàn toàn chính xác như được hiển thị bên dưới những gì thực sự xảy ra là 24 bit quan trọng nhất từ ​​giá trị nhị phân của số int bị loại bỏ để lại sự nhầm lẫn nếu bit ngoài cùng bên trái được đặt chỉ định số là âm

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
Tôi chưa bao giờ thấy điều đó trong bất kỳ cuốn sách nào trong 46 năm.
Hầu tước Lorne

2

Một thuật toán nhanh mô phỏng cách thức hoạt động của nó là như sau:

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

Làm thế nào điều này làm việc? Nhìn vào câu trả lời daixtr . Việc thực hiện thuật toán chính xác được ghi lại trong câu trả lời của ông là như sau:

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

Nếu bạn muốn hiểu điều này một cách toán học, như cách nó hoạt động

vì vậy về cơ bản, các số b / w -128 đến 127 sẽ được viết giống như giá trị thập phân của chúng, cao hơn số đó (số của bạn - 256).

ví dụ. 132, câu trả lời sẽ là 132 - 256 = - 124 tức là

256 + câu trả lời của bạn trong số 256 + (-124) là 132

Một vi dụ khac

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

đầu ra sẽ là 39 44

(295 - 256) (300 - 256)

LƯU Ý: nó sẽ không xem xét các số sau số thập phân.


0

Về mặt khái niệm, phép trừ lặp lại 256 được thực hiện cho số của bạn, cho đến khi nó nằm trong phạm vi -128 đến +127. Vì vậy, trong trường hợp của bạn, bạn bắt đầu với 132, sau đó kết thúc với -124 trong một bước.

Tính toán, điều này tương ứng với việc trích xuất 8 bit có ý nghĩa nhỏ nhất từ ​​số ban đầu của bạn. (Và lưu ý rằng bit quan trọng nhất trong số 8 này trở thành bit dấu.)

Lưu ý rằng trong các ngôn ngữ khác, hành vi này không được xác định (ví dụ: C và C ++).


Để rõ ràng, kết quả bạn nhận được giống như khi thực hiện phép trừ lặp đi lặp lại. Trong thực tế, JVM không thực sự làm theo cách này. (Nó sẽ không hiệu quả khủng khiếp!)
Stephen C

Thật. Tôi hy vọng đoạn thứ hai của tôi trình bày cách JVM thực sự làm điều này. Nhưng tôi đã thay đổi ngôn ngữ của mình một chút.
Bathsheba

1
Đúng. Sự thay đổi "về cơ bản" thành "về mặt khái niệm" tạo nên sự khác biệt rất lớn!
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           

Câu trả lời đúng được đưa ra bằng cách che dấu bit, không phải bằng cách chia và phần còn lại.
Hầu tước Lorne
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.