大整数转换为字节数组

24

我需要将一个Java BigInteger 实例转换为它的字节数值。从API中,我可以使用 toByteArray() 方法,该方法返回一个包含此 BigInteger 的二进制补码表示形式的 byte[]。

由于我的所有数字都是正128位(16字节)整数,因此我不需要得到包括符号位在内的2进制补码形式,该形式会给我129位(128位+符号位)……

是否有一种方法直接从BigInteger获取标准(无2进制补码形式)表示形式?

如果没有,那么我该如何对整个byte [17]数组进行右移操作以丢弃符号位,以便获得一个byte[16]数组?


就位移操作而言,我假设你已经阅读了Java中<<<、<<、>>、>>>运算符的相关资料了吧? - Martijn Verburg
2
@Martijn:差不多了;Java 中没有 <<< 操作符。 - musiKk
1
@musiKk 你说得对!这只是我一厢情愿的想法;p - Martijn Verburg
128位是6个字节吗?你确定吗? - TonyK
@Kami,你不能拥有一个128位整数,你只能拥有一个byte[],请看下面的答案。不需要移位。在我看来,区别只是概念上的。 - Peter Lawrey
显示剩余2条评论
4个回答

38

你根本不需要进行移位。符号位是字节数组中最重要的(即最左边)位。由于你知道你的数字始终为正数,因此保证符号位为0。但是,整个数组是右对齐的。

因此,有两种情况:你最左边的字节是0x00或者不是。如果是0x00,你可以安全地将其丢弃:

byte[] array = bigInteger.toByteArray();
if (array[0] == 0) {
    byte[] tmp = new byte[array.length - 1];
    System.arraycopy(array, 1, tmp, 0, tmp.length);
    array = tmp;
}
如果它不是0,那么你就不能将其删除 - 但是你的数组已经处于你想要的表示形式中,所以你不需要做任何事情。
上述代码应该适用于两种情况。

3
SO用户roman-nikitchenko指出,整个if语句的内容可以简化为一行代码:array = Arrays.copyOfRange(array, 1, array.length);。使用这种变化,就不需要声明一个tmp数组了。这是一个很好的提示,非常感谢! :-) - Thomas
@owlstead - 很好的评论。使用现有库函数而不是自己的实现的另一个原因当然是前者已经被广泛测试过了。对于像这样的短代码片段,这可能并不那么相关,当然你仍然可能在调用该库方法时出错。可读性当然也应该牢记在心,但这可以说是因人而异:有些人可能会觉得调用一个命名良好的方法更易读,而其他人可能更喜欢更明确的表述,例如,如果他们以前从未遇到过copyOfRange方法。 - Thomas
虽然我已经在多个项目中看到了这段代码,声称可以返回无符号字节数组,但我想要理解上述代码是否真的返回无符号字节数组。假设最左边的字节为0x00,当我们移除它时,那么有什么保证字节数组中下一个字节,也就是从左到右第二个字节不是负数,即该字节中的符号位不是“1”?如果我们尝试从新的字节数组构造大整数,将获得完全不同的数字。因此问题是如果最左边的字节为0x00,则删除它安全的原因是什么? - Tito
Thomas,也许我有所疏漏,但我已经再次阅读了这个问题,即使所有的数字都是正数,第一个字节可能是0x00,为什么丢弃它是安全的呢?如果我尝试从新的字节数组中创建一个大整数,即没有0x00字节的数组,那么得到的大整数将完全不同。或者我在这里有所遗漏吗? - Tito
@Tito 我认为你说得很有道理——这实际上取决于你如何解释字节数组中的0和1序列。根据我对问题的理解(特别是第3段:“不使用2的补码形式”),OP不希望将输出解释为2的补码,而是普通的二进制表示法。因此,你的担忧对他/她来说并不是问题。如果你的设置不同,并且你希望字节数组包含有效的2的补码表示,那么也没有必要担心:toByteArray不会返回不必要的前导0x00字节。 - Thomas
显示剩余4条评论

4

字节数组中的第一个(最重要的)字节可能不仅包含符号位,还包含普通位。

例如,这个 BigInteger:

new BigInteger("512")
    .add(new BigInteger("16"))
    .add(new BigInteger("1"));

这个二进制模式为:

00000010 00010001

也就是说,最高字节(带有符号位)同样具有您期望的“普通”位。

那么,您想要得到什么?

00000010 00010001 (what you have) or
00000100 0010001? or
10000100 01??????

3
你可以复制第一个字节。或者你可以忽略它。
BigInteger bi = BigInteger.ONE.shiftLeft(127);
byte[] bytes1 = bi.toByteArray();
System.out.println(Arrays.toString(bytes1));
byte[] bytes = new byte[bytes1.length-1];
System.arraycopy(bytes1, 1, bytes, 0, bytes.length);
System.out.println(Arrays.toString(bytes));

0

如果您想要了解流行的GMP库的字节数组表示,您需要按照上述文档中所述删除前导零,并且将数组翻转以使最高有效字节位于末尾。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接