Java右移操作输出负值

huangapple go评论116阅读模式
英文:

Java shift right outputting negative value

问题

我真的很困惑,最好的办法是先展示一下我的代码,这是我得到的代码:

void dumpInt(int x) throws IOException {
    if (true) {
        //8388638
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        writer.writeByte(x & 0xff);
        writer.writeByte((x >> 8) & 0xff);
        writer.writeByte((x >> 16) & 0xfff);
        writer.writeByte((x >> 24) & 0xff);
        outputStream.write(x & 0xff);
        outputStream.write((x >> 8) & 0xff);
        outputStream.write((x >> 16) & 0xff);
        outputStream.write((x >> 24) & 0xff);
        System.out.println((x >> 16) & 0xff);
        if (x == 8388638) {
            String xz = "";
            byte[] array = outputStream.toByteArray();
            System.out.println(array[2] + " | " + (char) array[2]);
        }
    } else {
        writer.writeInt(x);
    }
}

这个代码运行得很好,但是当我执行dumpInt(8388638)时,出现了一些奇怪的情况。writer.writeByte((x >> 16) & 0xfff) 将 -128 写入了 writer。我不知道为什么会这样。

对于 outputStream.write((x >> 16) & 0xff); 也是相同的情况,但是当我运行 System.out.println((x >> 16) & 0xff); 时,它输出 128(正数)。

有人知道这是为什么,以及我如何进行修正吗?
(抱歉,我在 Java 方面有些经验,但并不是最好的,所以如果这是一个非常简单的修复方法,我很抱歉)

英文:

i'm really confused with, well it's best if I show my code first so here's what I got.

void dumpInt(int x) throws IOException {
	if ( true ) {
		//8388638
		ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
		writer.writeByte(x&0xff);
		writer.writeByte((x>>8)&0xff);
		writer.writeByte((x >> 16) & 0xfff);
		writer.writeByte((x>>24)&0xff);
		outputStream.write(x & 0xff);
		outputStream.write((x >> 8) & 0xff);
		outputStream.write((x >> 16) & 0xff);
		outputStream.write((x >> 24) & 0xff);
		System.out.println((x >> 16) & 0xff);
		if(x == 8388638){
			String xz = "";
			byte[]array = outputStream.toByteArray();
			System.out.println(array[2]+" | " + (char)array[2]);
		}
	} else {
		writer.writeInt(x);
	}
}

this works fine but when I do dumpInt(8388638), I get some weird occurrences.
writer.writeByte((x >> 16) & 0xfff) writes -128 to the writer
Im clueless as why that is.

The same thing happens for outputStream.write((x >> 16) & 0xff); but when I run System.out.println((x >> 16) & 0xff); it outputs 128(positive)

Does anybody know why this is and how I can correct it?
(Sorry i'm somewhat experienced in java but im not the best by any means so if this is a really simple fix my bad)

答案1

得分: 1

在Java中,一个字节的范围是[-128,+127],也就是说,+128是绝对不可能的。事实上,以-128打印的就是存储+128的字节(从位的角度看,+128表示为1000 0000,作为Java字节,因为它们是有符号的,所以会被解释为-128,因为也是1000 0000)。

换句话说,你的应用程序运行正常。

你评论中的链接图片是因为你误解了Java中括号和运算符优先级的工作方式。

System.out.println((byte)(x>>16)&0xff);

解析如下:

(byte)(x>>16) & 0xFF

由于0xFF是一个int(所有数字文字在Java中始终是int,除非以大写L结尾,那么它们将是long。你不能在Java代码中使用byte或short文字),这是一个字节和int之间的操作。与所有字节和int之间的操作一样,首先将字节升级为int,然后通过在两个得到的int上执行操作来返回结果。换句话说,实际上是这样的:

(int)((byte)(x>>16) & 0xFF

这将得到+128。

尝试一下这个:

(byte)((x>>16) & 0xFF)。你会发现它按预期打印出-128

英文:

In java, a byte has the range of [-128, +127], as in, +128 is literally impossible. In fact, -128 is how a byte that holds +128 is printed (in the sense that +128 is, in bits, 1000 0000, and as a java byte, as they are signed, that is rendered as -128, because that's also 1000 0000.

In other words, your app is working fine.

Your linked picture in your comment is due to you misunderstanding how parentheses and operator priority works in java.

> System.out.println((byte)(x>>16)&0xff);

resolves like so:

(byte)(x>>16) & 0xFF

and as 0xFF is an int (all numeric literals are always an int, unless they end in a capital L, then they are a long. You can't put a byte or short literal in java code), that's an operation between a byte and an int, and like all ops between bytes and ints, this is resolved by first upgrading the byte to an int, and then returning the result by doing the op on the two resulting ints. In other words, it is this:

(int)((byte)(x>>16) & 0xFF

And that'd be +128.

Try this:

(byte)((x>>16) & 0xFF). You'll find it prints -128 as expected.

答案2

得分: 1

Java中的字节(Bytes)是有符号(signed)的,并且范围为-128到127。你似乎更熟悉字节的另一种十进制表示方式——无符号字节,范围从0到255。然而,在Java中并不存在独立的无符号字节数据类型。

以下显示了两种表示之间的相应值:

  有符号     -128 -127 -126 ... -1   0   1   ...   126 127 128
无符号      128  129  130  ... 255  0   1   ...   126 127 128

注意:这些在二进制中实际上并不是不同的表示。它们具有相同的二进制表示。这取决于你是否将最高位(MSB)解释为符号位。

你可以通过Byte.toUnsignedInt方法从有符号版本转换为无符号版本,也可以通过将其强制转换为byte来从无符号版本转换为有符号版本。

由于某些原因(你可能需要问API设计者),OutputStream.write接受无符号字节(以及有符号字节)。如果byte的范围只从-128到127,Java是如何做到这一点的呢?它接受的是int而不是byte!这就是你的代码为什么能够编译的原因。

write方法(writeByte也是调用它)将int值128转换为(有符号的)byte值-128,并将其写入流中。当你稍后获取写入的字节时,实际上你得到的是转换后的有符号byte。但实际上,在位级别上,128和-128是相同的1000 0000。只是Java坚持将第一个1解释为符号位。

正如我之前所说,如果你更喜欢另一种表示方式,你确实可以将-128转换为128

Byte.toUnsignedInt(-128) // 128

需要注意的是,结果必须是int,因为结果超出了byte的范围。

英文:

Bytes in Java are signed, and have the range -128~127. You seem to be more familiar with the other decimal representation of a byte - the unsigned byte, having a range from 0 to 255. However, that doesn't exist in Java as a separate data type.

The following shows corresponding values between the two representations:

  signed    -128 -127 -126 ... -1   0   1   ...   126 127 128
unsigned    128  129  130  ... 255  0   1   ...   126 127 128

Note: these are not really different representations in binary. They have the same binary representation. It depends on whether you interpret the MSB as the sign bit or not.

You can convert from the signed version to the unsigned version by Byte.toUnsignedInt, and you can convert from the unsigned version to the signed version by casting to byte.

For some reason that you'd have to ask the API designers, OutputStream.write accepts unsigned bytes (as well as signed ones). How does Java do this if byte only ranges from -128 to 127? It accepts an int rather than a byte! This is the reason why your code compiles at all.

write (which writeByte also calls) converts the int 128 to a (signed) byte -128 and writes it into the stream. When you get the written bytes later, you are getting the converted signed byte. But in fact, in terms of the bits, 128 and -128 are the same 1000 0000. It's just that Java insists on interpreting the first 1 as a sign bit.

As I said before, you can convert the -128 to 128` if you prefer the other representation:

Byte.toUnsignedInt(-128) // 128

Note that the result must be an int, because the result is outside of byte's range.

答案3

得分: 1

以下是您要翻译的内容:

>对于outputStream.write((x >> 16) & 0xff); 也会发生类似的情况;但是当我运行System.out.println((x >> 16) & 0xff); 时,它输出128(正数)。是否有人知道其中的原因以及我如何进行更正?

int x = 8388638;

当您执行以下操作时:

ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
outputStream.write((x >> 16) & 0xff); 
System.out.println(outputStream.toByteArray()[0]);

输出的结果为-128,因为该值为0b1000_0000,这在字节的二补数形式中表示为-128(最高位为符号位)。

当您执行以下操作时:

System.out.println((x >> 16) & 0xff);

(x>>16) 仍然是一个int值,然后&操作会屏蔽掉前24位。因此,低位的8位会作为一个int值进行打印,而不涉及二补数形式。

欲了解更多信息,请查阅二补数

英文:

>The same thing happens for outputStream.write((x >> 16) & 0xff); but when I run System.out.println((x >> 16) & 0xff); it outputs 128(positive) Does anybody know why this is and how I can correct it?

int x = 8388638;

When you do the following:

ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
outputStream.write((x >> 16) & 0xff); 
System.out.println(outputStream.toByteArray()[0]);

You are printing a -128 because the value is 0b1000_0000 which is -128 in
two's complement form for a byte (the high order bit being the sign bit).

When you do this:

System.out.println((x >> 16) & 0xff);

(x>>16) is still an int and then the & masks off the top 24 bits. So the low order 8 bits print as an int and two's complement does not come into play.

For more information check out Two's Complement

huangapple
  • 本文由 发表于 2020年9月30日 08:12:25
  • 转载请务必保留本文链接:https://go.coder-hub.com/64129261.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定