【问题标题】:Java and unsigned valuesJava 和无符号值
【发布时间】:2013-01-02 20:55:36
【问题描述】:

我正在解析来自 DatagramSocket 的无符号位。我总共有 24 位(或 3 个字节)进入 - 它们是:1 个无符号 8 位整数,后跟一个 16 位有符号整数。但是java从不将任何超过有符号字节的内容存储到字节/字节数组中?当 java 接受这些值时,你会丢失最后的第 8 位吗?

DatagramSocket serverSocket = new DatagramSocket(666);
        byte[] receiveData = new byte[3]; <--Now at this moment I lost my 8th bit

        System.out.println("Binary Server Listing on Port: "+port);

        while (true)
        {
            DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);
            serverSocket.receive(receivePacket);
            byte[] bArray = receivePacket.getData();
            byte b = bArray[0];

        }

自从我把它变成一个字节后,我现在是否丢失了这第 8 位?我初始化了一个 3 字节的字节数组是不是错了?

【问题讨论】:

  • 我希望有无符号变量(不仅仅是char)会使一些代码更容易,并允许编译器进行一些优化,仅对无符号值有效。
  • @MrSmith 编译器可以做它需要做的任何事情,因为所有对有符号值的操作在 Java 中都是明确定义的。
  • @starblue:我的意思是对于无符号值,允许进行一些优化,而对于有符号值是不允许的。例如 a % 4 可以优化为仅对正数有效的 a &amp; 2。 => 不允许 javacompiler 进行这种优化,即使开发人员只使用正数。
  • @MrSmith 你说得对,无符号除法是少数几个不容易用有符号数的运算来模拟的事情之一。 Java 8 将通过添加无符号数的缺失操作来解决这个问题:blogs.oracle.com/darcy/entry/unsigned_api

标签: java bit-manipulation


【解决方案1】:

当 java 接受这些值时,你会丢失最后的第 8 位吗?

没有。当它被设置时,你最终会得到一个负值。

所以要获得一个介于 0 和 255 之间的值,最简单的方法是使用如下内容:

int b = bArray[0] & 0xff;

首先将byte 提升为int,这将对其进行符号扩展,如果原始值的高位为1,则导致25 个前导1 位。 &amp; 0xff 然后再次摆脱前 24 位 :)

【讨论】:

  • 如果空间是个问题,您不必转换为int。您仍然可以使用byte 并将其视为二进制数据。
  • @Code-Guru 你能证明这一点吗?感谢您的回复
  • @stackoverflow:如果不知道你想用这个值做什么,很难证明它。但数据本身确实是安全的。
  • @stackoverflow 我不确定你想让我演示什么。 byte 中的数据是二进制位。更多的是关于您思考数据的方式,而不是计算机实际存储数据的方式。
  • @stackoverflow 看到我的回答。我已经对无符号值进行了相当多的操作(通过处理无符号值常见的网络数据包)并且我已经弄清楚了真正发生的事情。而且不漂亮……
【解决方案2】:

不,您不会丢失第 8 位。但不幸的是,Java 有两个“特性”使得处理这些值变得非常困难:

  • 其所有原始类型均已签名;
  • 当将一个原始类型“展开”为另一个更大尺寸的原始类型时(例如,将byte 读取为int,就像这里的情况一样),“较低类型”的符号位被扩展.

这意味着,例如,如果您读取字节0x80,它的二进制转换为:

1000 0000

当您将其读取为整数时,您会得到:

1111 1111 1111 1111 1111 1111 1000 0000
                              ^
                              This freaking bit gets expanded!

而你真正想要的:

0000 0000 0000 0000 0000 0000 1000 0000

即整数值 128。因此,您必须屏蔽它:

int b = array[0] & 0xff;

1111 1111 1111 1111 1111 1111 1000 0000 <-- byte read as an int, your original value of b
0000 0000 0000 0000 0000 0000 1111 1111 <-- mask (0xff)
--------------------------------------- <-- anded, give
0000 0000 0000 0000 0000 0000 1000 0000 <-- expected result

悲伤,但真实。

更一般地说:如果你想操作大量面向字节的数据,我建议你看看ByteBuffer,它会很有帮助。但不幸的是,这不会让您免于位掩码操作,只是它可以更轻松地读取给定数量的字节作为时间(作为原始类型)。

【讨论】:

  • 对您的演示非常有帮助且内容丰富。非常感谢您的回复
  • @stackoverflow 除了我在 16 位上编码了一个 int 而它是 32 :) 已修复,但原理保持不变。
  • 是的,我注意到了,但我明白了你想说的话。再次感谢
【解决方案3】:

在Java 中,字节(以及short、int 和long)只是一个有符号 数字数据类型。但是,这并不意味着将它们视为 unsigned 二进制数据时会丢失任何数据。如您的插图所示,10000000-128 作为带符号的十进制数。如果你处理的是二进制数据,只要把它当作它的二进制形式就行了。

【讨论】:

  • 顺便说一句:char是一个无符号数据类型
  • @MrSmith42 感谢您的收获。我对我的陈述进行了限定,希望更准确。
  • charnumeric unsigned 数据类型,值为 0 .. 65535 。但我认为它不应该用作通用数字类型。
  • @MrSmith42 我试图避免冗长,并可能遗漏适用的类型。希望我的最新编辑更准确。你怎么看?
猜你喜欢
  • 2011-10-11
  • 1970-01-01
  • 1970-01-01
  • 2013-02-10
  • 2014-02-01
  • 1970-01-01
  • 2011-04-09
  • 2012-09-30
  • 1970-01-01
相关资源
最近更新 更多