将二进制字符串转换为整数时出现NumberFormatException

时间:2018-07-04 06:47:06

标签: java numberformatexception string-conversion

我正在学习Java中的位操作。所以,我正在将二进制字符串转换为整数字节和短。 这是我的程序:-

byte sByte,lByte; // 8 bit
short sShort,lShort; // 16 bit
int sInt,lInt; // 32 bit
sByte= (byte)(int)Integer.valueOf("10000000", 2); //Smallest Byte
lByte= (byte) ~sByte;
sShort= (short)(int)Integer.valueOf("1000000000000000", 2); //Smallest Short
lShort = (short) ~sShort;
sInt = (int) (int)Integer.valueOf("10000000000000000000000000000000", 2); //Smallest Int
lInt= (int)~sInt;
System.out.println("\"10000000\" \"8 bit\" byte=>"+sByte+"\t~byte=>"+lByte);
System.out.println("\"1000000000000000\" \"16 bit\" short=>"+sShort+"\t~short=>"+lShort);
System.out.println("\"10000000000000000000000000000000\" \"32 bit\" int=>"+sInt+"\t~int=>"+lInt);

正在转换字节和短整数,并在不转换整数的情况下提供最小的字节和最小的短值。

它抛出NumberFormatException,如下所示:-

Exception in thread "main" java.lang.NumberFormatException: For input string: "10000000000000000000000000000000"
    at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
    at java.lang.Integer.parseInt(Integer.java:583)
    at java.lang.Integer.valueOf(Integer.java:740)
    at com.learnjava.BitsManipulation.start(BitsManipulation.java:14)
    at com.learnjava.learnjava.main(learnjava.java:9)

如果我评论这3条整数转换行:-

//    sInt = (int) (int)Integer.valueOf("10000000000000000000000000000000", 2); //Smallest Int
//    lInt= (int)~sInt;
//    System.out.println("\"10000000000000000000000000000000\" \"32 bit\" int=>"+sInt+"\t~int=>"+lInt);

它给了我输出:-

"10000000" "8 bit" byte=>-128   ~byte=>127
"1000000000000000" "16 bit" short=>-32768   ~short=>32767

这很好。我已经检查了两次字符串的长度,这是32位长度,整数在Java中是32位,这样它应该可以工作,但在这里不是这种情况。

如果我从32个长度的字符串中删除一个0或1或将1替换为0,那么它也可以正常工作,如下所示:-

sInt = (int) (int)Integer.valueOf("00000000000000000000000000000000", 2); //Smallest Int
lInt= (int)~sInt;
System.out.println("\"00000000000000000000000000000000\" \"32 bit\" int=>"+sInt+"\t~int=>"+lInt);


sInt = (int) (int)Integer.valueOf("1000000000000000000000000000000", 2); //Smallest Int
lInt= (int)~sInt;
System.out.println("\"1000000000000000000000000000000\" \"31 bit\" int=>"+sInt+"\t~int=>"+lInt);

输出如下:-

"00000000000000000000000000000000" "32 bit" int=>0  ~int=>-1

"1000000000000000000000000000000" "31 bit" int=>1073741824  ~int=>-1073741825

我对此感到困惑。 请告诉我为什么会引发NumberFormatException以及此问题的解决方案。

1 个答案:

答案 0 :(得分:3)

简短回答:

Integer.parseInt()方法(由Integer.valueOf()调用的方法)要求您告诉数字是否为负数,因为它不是设计用来处理带符号的数字。通过添加减号“固定”您的代码:

Integer sInt = Integer.valueOf("-10000000000000000000000000000000", 2);
System.out.println(sInt); // prints -2147483648

长答案:

您的stacktrace会告诉您在哪里可以找到问题所在。您的Integer.valueOf()方法将调用Integer.parseInt(),这将在583行上引发NumberFormatException。您可以在openjdk中找到Integer类的代码。

public static int parseInt(String s, int radix) throws NumberFormatException {
    int result = 0;
    int i = 0, len = s.length();
    int limit = -Integer.MAX_VALUE;
    int multmin;
    int digit;

    if (len > 0) {
        char firstChar = s.charAt(0);
        if (firstChar < '0') { // Possible leading "+" or "-"
            if (firstChar == '-') {
                negative = true;
                limit = Integer.MIN_VALUE;
            } else if (firstChar != '+')
                throw NumberFormatException.forInputString(s);
        }

        multmin = limit / radix;
        while (i < len) {
            digit = Character.digit(s.charAt(i++),radix);
            if (result < multmin) {
                throw NumberFormatException.forInputString(s);
            }
            result *= radix;
            result -= digit;
        }
    } else {
        throw NumberFormatException.forInputString(s);
    }
    return negative ? result : -result;
}

为了清楚起见,我删除了当前示例未执行的部分代码。请注意,限制为-2147483647(超过MIN_VALUE的一个)!同样,multmin为-1073741823。在循环的第一次迭代中,结果为0,因此result * radix为0,数字为1(这是我们字符串中的第一个数字),因此结果-数字为-1。在第二次迭代中,digit变为0,但是现在结果*基数为-2。在第三次迭代中,我们得到-4,然后是-8,依此类推...一直持续到结果等于-1073741824,这比我们的极限小一。

现在,我们还可以看到该方法检查是否在数字上添加了符号(+或-)。有趣的是,如果我们加上减号,我们会看到该限制设置为MIN_VALUE。因此,违反直觉,我们可以通过添加减号来简单地“修复”您的代码:

Integer sInt = Integer.valueOf("-10000000000000000000000000000000", 2);
System.out.println(sInt); // prints -2147483648

另一方面,您在此处进行了一些奇怪的转换。代替:

sByte= (byte)(int)Integer.valueOf("10000000", 2);

您应该能够写:

sByte = Byte.valueOf("10000000", 2);

我说“应该”,因为实际上你做不到。这导致另一个异常!这是因为Byte.valueOf()方法只调用Integer.valueOf()方法,然后将答案转换为Byte!而且因为整数的10000000为000 ... 00010000000 = 128,它将告诉您对于一个字节来说太大了。

哇!那么,为什么您怪异的铸造技巧起作用了?因为所谓的“无声溢出”。 Java意识到您迫切希望将数字128容纳在一个字节中,这是不可能的,因此它将尽可能多的数字放入其中(127),并通过添加其余的1绕到底部,从而使-128 。我想这就是设计静默溢出的目的,但是依赖它绝对不是一个好主意。为了说明您的行中发生的情况:

sByte= (byte)(int)Integer.valueOf("10000000", 2);

Integer a = Integer.valueOf("10000000", 2);
System.out.println(a);        // 128
int b = (int)a;
System.out.println(b);        // 128
byte sByte = (byte)b;
System.out.println(sByte);    // -128

因此,请勿在带符号的数字上使用parseInt()或valueOf()。