[随笔]Integer.parseInt转换二进制数为int整数异常

查看下面的Java示例,首先将java中的负整数转换为二进制类型:

int n = -255;
String nb = Integer.toBinaryString(n);
System.out.println(n + "二进制表示:" + nb);

控制台输出一个32位的二进制串:

-255二进制表示:11111111111111111111111100000001

然后尝试将这个二进制串原封不动的转换回去:

int pnb1 = Integer.parseInt(nb, 2);
System.out.println(nb + "使用parseInt转换:" + pnb1);

这时控制台报错,抛出NumberFormatException异常:

Exception in thread "main" java.lang.NumberFormatException: For input string: "11111111111111111111111100000001"
	at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
	at java.lang.Integer.parseInt(Integer.java:583)
	at com.itbo.test.Test0903.main(Test0903.java:14)

查看Integer.parseInt()源码:

public static int parseInt(String s, int radix)
			throws NumberFormatException
{
	/*
	 * WARNING: This method may be invoked early during VM initialization
	 * before IntegerCache is initialized. Care must be taken to not use
	 * the valueOf method.
	 */

	if (s == null) {
		throw new NumberFormatException("null");
	}

	if (radix < Character.MIN_RADIX) {
		throw new NumberFormatException("radix " + radix +
										" less than Character.MIN_RADIX");
	}

	if (radix > Character.MAX_RADIX) {
		throw new NumberFormatException("radix " + radix +
										" greater than Character.MAX_RADIX");
	}

	int result = 0;
	boolean negative = false;
	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);

			if (len == 1) // Cannot have lone "+" or "-"
				throw NumberFormatException.forInputString(s);
			i++;
		}
		multmin = limit / radix;
		while (i < len) {
			// Accumulating negatively avoids surprises near MAX_VALUE
			digit = Character.digit(s.charAt(i++),radix);
			if (digit < 0) {
				throw NumberFormatException.forInputString(s);
			}
			if (result < multmin) {
				throw NumberFormatException.forInputString(s);
			}
			result *= radix;
			if (result < limit + digit) {
				throw NumberFormatException.forInputString(s);
			}
			result -= digit;
		}
	} else {
		throw NumberFormatException.forInputString(s);
	}
	return negative ? result : -result;
}

发现抛出异常的代码如下:

if (result < multmin) {
    throw NumberFormatException.forInputString(s);
}

分析源码可知,对于Integer.parseInt()方法,它是按照符号的"+-"来确定转换后整数的正负,如果转换的参数没有符号则默认转换后的整数符号为正。需要注意的是Java中的数值类型都是有符号的,不存在无符号的数,它们的取值范围是固定的,不会随着硬件环境或操作系统的改变而改变。对于int整型来说取值范围固定为 [-2^31, 2^31-1] (-2,147,483,648 ~ 2,147,483,647)。

在操作系统里面定义二进制数的最高位代表符号位,但在计算机系统里面二进制负数是按照其补码存储的,这里可以忽略符号位。例如32位的-255在计算机中二进制表示为11111111111111111111111100000001,其实际代表的无符号数为4,294,967,041(已超出int整型取值范围),二者互为模2^32的补数。

回到parseInt()方法,对于参数-255的二进制串进行转换时正是按照其所代表的无符号数的数值进行转换的,由于没有提供+、-符号所以符号取正,但是它所代表的无符号数已经超出了int整型的取值范围,所以会抛出转换异常。

为了解决这个问题我们可以使用取值范围更大的Long(长整型)64位二进制补码整数:

long pnb2 = Long.parseLong(nb, 2);
System.out.println(nb + "使用parseLong转换:" + pnb2);
System.out.println("long型强制转换为int型: " + (int)pnb2);

long长整型占8个字节,变量pnb2对应的二进制表示为:

00000000 00000000 00000000 00000000 11111111 11111111 11111111 00000001

由于int整型只占4个字节,在强制转换为int整型时只截取低32位字节变为:

11111111 11111111 11111111 00000001

11111111111111111111111100000001 是-255的补码,因此强制转换后的值为-255,控制台输出:

-255二进制表示:11111111111111111111111100000001
11111111111111111111111100000001使用parseLong转换:4294967041
long型强制转换为int型: -255

对于Java 8,它引入了支持unsigned无符号数操作的新API:Integer.parseUnsignedInt() 

int pnb3 = Integer.parseUnsignedInt(nb, 2);
System.out.println(nb + "使用parseUnsignedInt转换:" + pnb3);

控制台输出:

-255二进制表示:11111111111111111111111100000001
11111111111111111111111100000001使用parseUnsignedInt转换:-255

下面贴出Integer.parseUnsignedInt()的源码:

public static int parseUnsignedInt(String s, int radix)
	throws NumberFormatException {
	if (s == null)  {
	    throw new NumberFormatException("null");
	}

	int len = s.length();
	if (len > 0) {
	    char firstChar = s.charAt(0);
	    if (firstChar == '-') {
		throw new
		    NumberFormatException(String.format("Illegal leading minus sign " +
						       "on unsigned string %s.", s));
	    } else {
		if (len <= 5 || // Integer.MAX_VALUE in Character.MAX_RADIX is 6 digits
		    (radix == 10 && len <= 9) ) { // Integer.MAX_VALUE in base 10 is 10 digits
		    return parseInt(s, radix);
		} else {
		    long ell = Long.parseLong(s, radix);
		    if ((ell & 0xffff_ffff_0000_0000L) == 0) {
			return (int) ell;
		    } else {
			throw new
			    NumberFormatException(String.format("String value %s exceeds " +
								"range of unsigned int.", s));
		    }
		}
	    }
	} else {
		throw NumberFormatException.forInputString(s);
	}
}

可以发现parseUnsignedInt()方法中也是利用parseLong的结果强制转换为int整型作为return值。

参考文章

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值