这是Java Api的错误吗?
int i = 0xD3951892;
System.out.println(i); // -745203566
String binString = Integer.toBinaryString(i);
int radix = 2;
int j = Integer.valueOf(binString, radix );
Assertions.assertThat(j).isEqualTo(i);
我希望它毫无疑问是真的。但它抛出异常:
java.lang.NumberFormatException: For input string: "11010011100101010001100010010010"
at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
at java.lang.Integer.parseInt(Integer.java:495)
at java.lang.Integer.valueOf(Integer.java:556)
at com.zhugw.temp.IntegerTest.test_valueof_binary_string(IntegerTest.java:14)
所以,如果我有二进制字符串,例如11010011100101010001100010010010,如何在Java中获取其十进制数(-745203566)? DIY?编写代码来实现下面的等式?
答案 0 :(得分:7)
Integer.valueOf(String, int radix)
和Integer.parseInt(String, int radix)
只会解析值-2 147 483 648到2 147 483 647的数字,即32位有符号整数的值。
这些函数无法解释二进制(radix = 2
)的二进制补码数,因为传递的字符串可以是任意长度,因此前导1可以是数字或符号位的一部分。我猜Java的开发人员认为最合乎逻辑的方法是永远不要接受两个补码,而不是假设第32位是符号位。
他们将您的输入二进制字符串读取为无符号3 549 763 730(大于max int值)。要读取负值,您需要提供前面带有-
符号的正二进制数。例如-5
:
Integer.parseInt("1011", 2); // 11
// Even if you extended the 1s to try and make two's complement of 5,
// it would always read it as a positive binary value
Integer.parseInt("-101", 2); // -5, this is right
解决方案:
我建议,首先,如果您可以将其存储为带有额外签名信息的正数(例如-
符号),请执行此操作。例如:
String binString;
if(i < 0)
binString = "-" + Integer.toBinaryString(-i);
else // positive i
binString = Integer.toBinaryString(i);
如果你需要使用带符号的二进制字符串,为了以二进制二的补码形式(作为一个字符串)取一个负数并将其解析为一个int,我建议你采用这两个字符串。手动补码,将其转换为int,然后更正符号。回想一下,两个补码=一个补码+ 1,而一个补码只是反转每个位。
作为示例实现:
String binString = "11010011100101010001100010010010";
StringBuilder onesComplementBuilder = new StringBuilder();
for(char bit : binString.toCharArray()) {
// if bit is '0', append a 1. if bit is '1', append a 0.
onesComplementBuilder.append((bit == '0') ? 1 : 0);
}
String onesComplement = onesComplementBuilder.toString();
System.out.println(onesComplement); // should be the NOT of binString
int converted = Integer.valueOf(onesComplement, 2);
// two's complement = one's complement + 1. This is the positive value
// of our original binary string, so make it negative again.
int value = -(converted + 1);
您还可以为32位二进制补码二进制数编写自己的Integer.parseInt
版本。当然,这假设您不使用Java 8而且不能只使用Integer.parseUnsignedInt
,@llogiq在输入时指出了这一点。
编辑:您也可以先使用Long.parseLong(String, 2)
,然后计算两个补码(并将其掩盖为0xFFFFFFFF),然后将long
降级为int
。编写速度更快,代码可能更快。
答案 1 :(得分:4)
Integer.toBinaryString(..)
的API文档明确声明:
可以通过调用
Integer.parseUnsignedInt(s, 8)
从返回的字符串中恢复参数的值。
(从Java 8u25开始)我认为这是一个文档错误,它应该是Integer.parseUnsignedInt(s, 2)
。请注意Unsigned
。这是因为toBinaryString
输出将包含符号位。
编辑:请注意,即使这看起来会产生无符号值,但它不是。这是因为Java实际上没有无符号值的概念,只有少数静态方法可以使用int,就好像它们 无符号一样。