来自字符串和字节数组的 BigInteger 值的差异

Difference in BigInteger values from a String and a byte array

谁能解释一下下面两个 BigInteger 初始化之间的区别。

输入:

BigInteger bi1 = new BigInteger("EF", 16);
byte[] ba = new byte[] {(byte)0xEF};
BigInteger bi2 = new BigInteger(ba);
Log.d("BIGINTEGER", "Big Integer1 = " + bi1.toString(16));
Log.d("BIGINTEGER", "Big Integer2 = " + bi2.toString(16));

输出:

Big Integer1 = ef
Big Integer2 = -11

如何使用字节数组中的值 "EF" 初始化 BigInteger?

byte[]:

中输入前导零
byte[] ba = new byte[] {0, (byte)0xEF};

Ideone demo

public BigInteger(byte[] val)

Translates a byte array containing the two's-complement binary representation of a BigInteger into a BigInteger. The input array is assumed to be in big-endian byte-order: the most significant byte is in the zeroth element.

public BigInteger(String val, int radix)

Translates the String representation of a BigInteger in the specified radix into a BigInteger. [...]

Source: Oracle Java 7 Docs

您从字节数组进行的初始化未按预期运行,因为 0xEF 转换为字节数组 returns {1, 1, 1, 0, 1, 1, 1, 1}。

根据上述规格做成整数如下:

1*2^0 + 1*2^1 + 1*2^2 + 1*2^3 + 0*2^4 + 1*2^5 + 1*2^6 - 1*2^7 = -17 = -0x11

补码导致最高字节被减去,而不是被添加。所以在字节数组的开头添加一个 0 应该可以解决问题:

byte[] ba = new byte[] {0, (byte)0xEF};

您需要在 byte[] 数组中添加一个零:

byte[] myByteArray = new byte[] {0, (byte)0xEF};
BigInteger bi2 = new BigInteger(ba);
Log.d("BIGINTEGER", "Big Integer1 = " + bi1.toString(16));
Log.d("BIGINTEGER", "Big Integer2 = " + bi2.toString(16));

为什么?

嗯,原因与语言规范有关:

十进制 文字有一个特殊的 属性 不被 十六进制 共享,即十进制文字都是正数 [JLS 3.10.1].

要写负十进制常量,需要结合使用一元求反运算符(-)和十进制字面量。

这样,你可以写入任何int或long值,无论是正数 或负数,十进制形式,负十进制常量可以通过减号清楚地识别。

十六进制或八进制文字并非如此。 它们可以取正值和负值。十六进制和八进制文字是 如果设置了高位,则为负。

所以说了之后,0xFE其实是一个负数...

来自BigInteger docs

Constructor and Description

BigInteger(byte[] val)

Translates a byte array containing the two's-complement binary representation of a BigInteger into a BigInteger.

补码才是真正的原因。

让我们看看如何...

(Byte)0xef 二进制 = 11101111 现在将其转换回 Int 并得到 -17(基数 10)或 -11(基数 16)。

现在看看

byte[] ba = new byte[] {0, (byte)0xEF};

这有 (Byte)0xef 但前面有 0。这意味着这个数组有 00000000 11101111,转换后会给出正确的结果。

为什么之前的案例不同?

查看 2 的补码规则 - SO Answer, Mandatory Wikipedia link

另一种思考方式

十进制的 0xEF = 239

字节范围 = -127 到 128

我们有溢出。

239 - 128 = 111

现在从后面数这个 111(数字数据类型具有这种循环行为,同样是由于 2 的补码表示)。

例如:129.toByte = -127

(129 - 128 = 1, 从后面数第一个值 = -127)

倒数的快捷方式if x>128 && x<256 then x.toByte = (x - 128) - 128

这里 x = 239 所以 x.toByte = -17