在字节数组中表示一个数字(java编程)

Posted

技术标签:

【中文标题】在字节数组中表示一个数字(java编程)【英文标题】:Representing a number in a byte array (java programming) 【发布时间】:2011-02-21 13:42:01 【问题描述】:

我试图在一个两字节数组中表示端口号 9876(或十六进制的 0x2694):

class foo 
     public static void main (String args[]) 
   byte[] sendData = new byte[1];

   sendData[0] = 0x26;
   sendData[1] = 0x94;
     

但是我收到了一个关于可能会丢失精度的警告:

foo.java:5: possible loss of precision
found   : int
required: byte
   sendData[1] = 0x94;
                 ^
1 error

如何在两字节数组中表示数字 9876 而不会丢失精度?

注意:我选择了@Björn 的代码作为正确答案,但@glowcoder 的代码也很好用。这只是解决同一问题的不同方法。谢谢大家!

【问题讨论】:

请注意,通过网络发送的扁平化数据结构通常使用 Java 中的 DataOutputStream(或其子类 ObjectOutputStream)来完成。无需每次都重新发明bitfiddling :-) @meriton: 更好的是,使用java.nio.ByteBuffer 可以做到这一切,不必处理 IOException,也可以控制字节序。 【参考方案1】:

您是否尝试过转换为字节?例如

sendData[1] = (byte)0x94;

【讨论】:

那会导致我的精度下降,不是吗? @Mark:它会让编译器清楚你正在做你想做的事。 0x94 在一个字节的范围内,所以这里没有实际的精度损失。 我以为一个字节的范围是-128到127,0x94是148 @Mark - 0x94 作为一个字节实际上不是 148,而是 -108。 @Mark:嗯……不。看位,一个字节的范围是0b00000000到0b11111111,0x94=0b10010100,正好在它的范围内。按位,“有符号字节”的概念没有任何意义。只有当您将八位序列解释为整数时,它才开始变得清晰。【参考方案2】:

0x94 是十进制的 148,超出了 java 中字节的范围(-128 到 127)。 您可以执行以下操作之一:

1) 强制转换可以正常工作,因为它将保留二进制表示(对于 0x00 到 0xFF,没有有意义的位被截断):

 sendData[1] = (byte)0x94; 

2) 0x94 作为有符号字节的二进制表示是-108(-0x6C),所以下面的效果是一样的:

sendData[1] = -0x6C; //or -108 in decimal

【讨论】:

要将有符号字节转换回其无符号整数形式,您可以简单地写b & 0xff【参考方案3】:

Björn 对使用流给出了一个很好的通用答案。你也可以使用java.nio.ByteBuffer 来做同样的事情,这会导致代码稍微少一些,并且你还可以控制输出的字节序(字节顺序)。

创建字节数组:

public static byte[] toByteArray(int bits) 
    ByteBuffer buf = ByteBuffer.allocate(4);
    buf.putInt(bits);
    return buf.array();

反转它:

public static int fromByteArray(byte[] b) 
    ByteBuffer buf = ByteBuffer.wrap(b);
    return buf.getInt();

【讨论】:

【参考方案4】:

我的第一个答案是位移,但再想一想,我认为使用输出流可能会更好,更容易理解。我通常避免强制转换,但如果您不寻求通用解决方案,我想这没关系。 :)

使用流,一个通用的解决方案:

public byte[] intToByteArray(final int i) throws java.io.IOException 
    java.io.ByteArrayOutputStream b = new java.io.ByteArrayOutputStream();
    java.io.DataOutputStream d = new java.io.DataOutputStream(b);
    d.writeInt(i);
    d.flush();

    return b.toByteArray();

然后反转它:

public int byteArrayToInt(final byte[] b) throws IOException 
    java.io.ByteArrayInputStream ba = new java.io.ByteArrayInputStream(b);
    java.io.DataInputStream d = new java.io.DataInputStream(ba);

    return d.readInt();

【讨论】:

您也可以使用 java.nio.ByteBuffer 进行一般性操作,并在必要时控制字节序。我已经为这种可能性添加了答案。【参考方案5】:

您必须转换为 (byte),因为 java 中的默认数字类型是 int,它大于 byte。只要值适合字节,就可以转换。

【讨论】:

【参考方案6】:

试试这个:

sendData[0] =(byte)0x26
sendData[1] =(byte)0x94

或者这个:

sendData[0] =(byte)38
sendData[1] =(byte)148

您必须将数据转换为字节才能将其分配给字节!

这并不意味着你失去了精度,只是写 0x26 意味着一个 int 到 Java 编译器..

但还要注意:一个字节的范围是从 -128 到 127,所以在 0x94=148 的情况下,它会在字节转换后表示为 '-108' ,所以它在数学计算中不能正常工作..

【讨论】:

【参考方案7】:

这是因为 Java 中的所有内容都已签名。 0x94 (148) 大于 Byte.MAX_VALUE(2^7-1)。

你需要的是

public static byte[] intToByteArray(int value) 
    byte[] b = new byte[4];
    for (int i = 0; i < 4; i++) 
        int offset = (b.length - 1 - i) * 8;
        b[i] = (byte) ((value >>> offset) & 0xFF);
    
    return b;

【讨论】:

注意,这会完成 int 的所有 4 个字节......你只需要其中的两个。剩下的就很简单了。 这有两个错误——这不是因为所有内容都已签名,而是因为 0x94 是一个 int,而不是一个字节,其次是 char 类型是无符号的,第三——该代码是一个笑话吗?如果你只是在和那个人打交道,那可能值得 +1 :) 如果你用 (byte) 沮丧的话,原来的很好 0x94 是一个 int,因为它不适合字节 because bytes are signed。你是正确的,字符是无符号的 - 但是,这是规则的例外,因为它不是 intended 是一个数字 - JavaGods 决定他们的所有数字都将被签名。你能像数字一样使用它来优化字符串吗?是的你可以。它是否打算用作数字?否。Character 类是否扩展了 Number 类?不。第三,不,这段代码不是开玩笑,这是将任何 int 转换为字节数组以准确表示其位的合法方式。过去我用它来创建 TCP/IP 数据包。 我试过你的代码,效果很好。感谢您帮助初学者学习 Java!

以上是关于在字节数组中表示一个数字(java编程)的主要内容,如果未能解决你的问题,请参考以下文章

int数组与单个int? [重复]

以一种有效的方式使用 ByteBuffer 将标题和数据布局打包在一个字节数组中?

如何在python 3中有效地将原始字节写入numpy数组数据

**在C语言中是做啥的? [复制]

在 Java 中,如何将字节数组转换为十六进制数字字符串,同时保持前导零? [复制]

在 Java 中,如何将字节数组转换为十六进制数字字符串,同时保持前导零? [复制]