Java将int转换为字节时的奇怪行为?


130
int i =132;

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

令人难以置信。为什么输出-124

Answers:


172

在Java中,an int是32位。A byte是8 bits

最原始的类型Java中的签名,byteshortint,和long被编码在二进制补码。(char类型为unsigned,并且sign的概念不适用于boolean。)

在此数字方案中,最高有效位指定数字的符号。如果需要更多位,则将最高有效位(“ MSB”)简单复制到新的MSB。

因此,如果您具有byte 25511111111 并将其表示为int32位,则只需将1复制到左侧24次。

现在,读取负数2的补码的一种方法是从最低有效位开始,向左移动直到找到第一个1,然后再将每一位取反。结果数字是该数字的正数

例如:11111111转到00000001= -1。这就是Java将显示为值的内容。

您可能想做的是知道字节的无符号值。

您可以使用位掩码完成此操作,该位掩码将删除除最低有效8位之外的所有内容。(0xff)

所以:

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

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

将打印出: "Signed: -1 Unsigned: 255"

这里到底发生了什么?

我们使用按位AND来掩盖所有无关的符号位(最低有效8位左侧的1。)将int转换为字节时,Java会将最左侧的24位砍掉

1111111111111111111111111010101
&
0000000000000000000000001111111
=
0000000000000000000000001010101

由于现在第32位是符号位,而不是第8位(并且我们将符号位设置为0,这是正数),因此Java读取字节中的原始8位作为正值。


1
做得好,韦恩(Wayne)在这个问题上最好的解释!我只是在寻找数学形式化,为什么可以在二进制补码表示形式中将符号位复制到右侧以添加位。考虑如何获得数字否定的规则很容易理解。就是说:考虑从右到左的所有位,并在写入第一个1之前保持不变。然后反转后续位。如果我认为丢失的位为0,就很容易理解它们全都为1。但是我正在寻找一个更“数学”的解释。
AgostinoX 2014年

令人讨厌的signedByte & (0xff)是,这0xff是一个整数文字,因此在执行按位运算之前,signedByte被提升为整数。
凯文·惠勒

那不是0xFF,在您的示例中是0x7E!
JohnyTex

89

132数字(以101000_0100以位(以2),Java存储int在32位中:

0000_0000_0000_0000_0000_0000_1000_0100

int到字节的算法是左截断的;算法System.out.println二进制补码(二进制补码是如果最左边的位是1,解释为负补码(反相比特)减一。); 因此System.out.println(int-to-byte( ))是:

  • 解释为(if-leftmost-bit-is-1 [negative(invert-bits(minus-one(] left-truncate(0000_0000_0000_0000_0000_0000_1000_0100)[)))])
  • = interpret-as(if-leftmost-bit-is-1 [negative(invert-bits(minus-one(] 1000_0100[ ] )))])
  • =解释为(负(反转位(减一(1000_0100))))
  • =解释为(负(反转位(1000_0011)))
  • = interpret-as(negative(0111_1100))
  • =解释-(负(124))
  • =解释为(-124)
  • = -124多田!!!

7
很好解释
-ZAJ

1
因此,现在小数点132为-124字节。反向如何工作?
Nilesh Deokar,

@NileshDeokar,相反的是POLA,因为它们适合(;参见JLS 5.1.2); 输出与左键符号一致(0正号和1负号)。
Pacerier,

什么是POLA?从int到a 的转换byte是有损转换(即信息丢失)。因此,无法将其转换回其原始int值。
trueadjustr

23

Java中的byte是带符号的,因此它的范围是-2 ^ 7到2 ^ 7-1-即-128到127。由于132在127之上,所以最终回绕到132-256 = -124。也就是说,基本上将256(2 ^ 8)加或减,直到它落入范围。

有关更多信息,您可能需要阅读二进制补码


16

132在-128到127(Byte.MIN_VALUE到Byte.MAX_VALUE)的字节范围之外。而是将8位值的高位视为带符号,表示在这种情况下为负。所以数字是132-256 = -124。


5

这是一种没有分心理论的非常机械的方法:

  1. 将数字转换为二进制表示形式(使用计算器好吗?)
  2. 仅复制最右边的8位(LSB),其余部分丢弃。
  3. 根据步骤2的结果,如果最左边的位为0,则使用计算器将数字转换为十进制。这就是你的答案。
  4. 否则(如果最左边的位是1),您的答案是否定的。保留所有最右边的零和第一个非零位不变。并将其余的取反,即将1替换为0,将0替换为1。然后使用计算器将其转换为十进制并附加一个负号以指示该值是负数。

这种更实用的方法符合上面的许多理论答案。因此,那些仍在阅读那些Java书籍说要使用模数的人,这绝对是错误的,因为我上面概述的4个步骤绝对不是模数运算。


哪些 Java书籍说要使用“模”?我从没看过CS书中提到的46年,更不用说Java书了。什么是“模”?Java中没有模运算。仅余数运算符。
洛恩侯爵,

grep更难了。http://iiti.ac.in/people/~tanimad/JavaTheCompleteReference.pdf第59页
trueadjustr

4

二进制补码公式:

在此处输入图片说明


在Java中,byte(N = 8)和int(N = 32)由上面显示的2s补码表示。

根据等式,a 7对而言为负,byte但对而言为正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

通常在书中,您会发现模数除法执行从int到byte的转换。如下图所示,这并不是严格正确的:实际发生的情况是,将int数的二进制值中的24个最高有效位丢弃了,如果剩余的最左边的位被设置为负数,则会造成混乱

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年中从未见过任何一本书。
洛恩侯爵,

2

一种模拟其工作方式的快速算法如下:

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

这是如何工作的?期待daixtr的答案。在他的答案中描述的精确算法的实现如下:

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

如果您想从数学上理解它,例如它是如何工作的

因此,基本上b / w -128至127的数字将与它们的十进制值相同,高于其十进制值(您的数字-256)。

例如。132,答案将是132-256 =-124,即

256 + 256中的答案(-124)为132

另一个例子

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

输出将是39 44

(295-256)(300-256)

注意:它将不考虑小数点后的数字。


0

从概念上讲,您的数字将重复减去256,直到它在-128到+127之间。因此,在您的情况下,您以132开始,然后以-124结尾。

计算上,这相当于从原始数字中提取8个最低有效位。(请注意,这8位中的最高有效位变为符号位。)

请注意,在其他语言中,未定义此行为(例如C和C ++)。


需要明确的是,你得到的结果是一样的,就好像重复减法做了。实际上,JVM实际上并不以这种方式这样做。(这将是非常低效的!)
Stephen C

确实。我希望我的第二段内容涵盖JVM实际上是如何做到的。但是我对我的语言有些摆弄。
Bathsheba

1
是。从“本质上”到“概念上”的变化带来了巨大的变化!
斯蒂芬·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           

正确的答案是通过位屏蔽而不是除法和余数得出的。
洛恩侯爵,
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.