Java中二进制和十进制整数之间相互转换的思考

    我的一个项目需要经常用到二进制和十进制整数之间的相互转换。这应该是个非常容易实现的问题,所用到的算法也极其简单。Java更是提供了Integer.toBinaryString(int number)方法直接获取某个十进制的二进制字符串表示。在二进制转换十进制方面可以使用Java的Integer.valueOf(String s, int radix); 这里的radix就是进制。Integer.valueOf(String s, 2);这条语句就能轻而易举的把字符串表示的二进制转成十进制整数。

    可见,在Java中完成二进制和十进制整数之间相互转换简直太easy了。
    由于项目中需要的二进制char数组表示而非字符串表示,于是我就直接把这两个方法简单进行封装得到以下代码:
import java.util.Arrays;

/**
 * 二进制和数字之间相互转换类
 * <pre>
 * email: yangzhengzheng1985@163.com
 * time: 2011/8/18 7:30
 * </pre>
 * @author 杨争争
 *
 */
public class BinaryCharArrayNumberTransfer1 {
	/**
	 * 转换整型为二进制char数组
	 * @param number	给定整数
	 * @param dimensionOfCharArray	二进制数组的位数
	 * @return	number的二进制组成的char数组
	 */
	public static char[] transferNumber2CharArray(int number,
			int dimensionOfCharArray) {
		char[] src = Integer.toBinaryString(number).toCharArray();
		char[] dest = new char[dimensionOfCharArray];
		System.arraycopy(src, 0, dest, dimensionOfCharArray - src.length,
				src.length);
		for (int i = 0; i < 32 - src.length; i++)
			dest[i] = '0';
		return dest;
	}

	/**
	 * 二进制char数组转换为整数
	 * @param ch	二进制数组
	 * @return	二进制表示的整数
	 */
	public static int transferCharArray2Number(char[] ch) {
		if (ch.length != 32)
			throw new RuntimeException("数组大小必须为32位");

		String	s = new String(ch);
			return Integer.valueOf(s, 2);
		
	}

	// for test
	public static void main(String[] args) {
		for (int i = -10; i <= 10; i++) {
			char[] ch = BinaryCharArrayNumberTransfer1.transferNumber2CharArray(
					i, 32);
			System.out.println("十进制转换二进制: " + i + "-->" + Arrays.toString(ch));
			int num = BinaryCharArrayNumberTransfer1
					.transferCharArray2Number(ch);
			System.out.println("二进制转换十进制: " + num);
			System.out.println("-----------");
		}
	}
}
    但是,在测试这两个方法的时候却得到了意外的结果。
    方法transferNumber2CharArray没有任何问题。问题主要出在transferCharArray2Number上,当传入的参数是一个负数的二进制char数组时,运行会报java.lang.NumberFormatException。用-10进行测试,得到以下结果:
    十进制转换二进制: -10-->[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0]
    Exception in thread "main" java.lang.NumberFormatException: For input string: "11111111111111111111111111110110"
    at java.lang.NumberFormatException.forInputString(NumberFormatException.java:48)
    at java.lang.Integer.parseInt(Integer.java:461)
    at java.lang.Integer.valueOf(Integer.java:528)
    at BinaryCharArrayNumberTransfer.transferCharArray2Number(BinaryCharArrayNumberTransfer.java:57)
    at BinaryCharArrayNumberTransfer.main(BinaryCharArrayNumberTransfer.java:67)
    这说明十进制被成功转换成了二进制,但是相应的二进制并没有成功转成十进制。因为在执行转换过程中报了 java.lang.NumberFormatException,转换过程被迫终止。去掉测试中的非正整数测试数据,异常消失,转换成功。这是为什么呢?
     查看jdk源代码,发现valueOf方法调用了parseInt方法。
 public static Integer valueOf(String s, int radix) throws NumberFormatException {
	return Integer.valueOf(parseInt(s,radix));
    }
public static int parseInt(String s, int radix)
		throws NumberFormatException
    {
        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, max = s.length();
	int limit;
	int multmin;
	int digit;

	if (max > 0) {
	    if (s.charAt(0) == '-') {
		negative = true;
		limit = Integer.MIN_VALUE;
		i++;
	    } else {
		limit = -Integer.MAX_VALUE;
	    }
	    multmin = limit / radix;
	    if (i < max) {
		digit = Character.digit(s.charAt(i++),radix);
		if (digit < 0) {
		    throw NumberFormatException.forInputString(s);
		} else {
		    result = -digit;
		}
	    }
	    while (i < max) {
		// 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);
	}
	if (negative) {
	    if (i > 1) {
		return result;
	    } else {	/* Only got "-" */
		throw NumberFormatException.forInputString(s);
	    }
	} else {
	    return -result;
	}
    }
    parseInt方法中有一句 if (s.charAt(0) == '-'),这是什么意思?传入的二进制字符串不应该只有0,1吗?这个"-"有什么特别之处吗?难道这个"-"是用来表示二进制的正负的?如果那样的话,Integer.toBinaryString(int)返回的就应该是无符号位的二进制字符串。马上查看Integer.toBinaryString(int)的源码。
public static String toBinaryString(int i) {
return toUnsignedString(i, 1);
}
    返回的的确是无符号位的二进制字符串。至此,原因找到了。看来之前并没有注意到JDK API的这些细节啊,让我这边排查了好一会,惭愧!

    当传入的参数是负整数的二进制的时候Integer.valueOf(String, 2)是不能用了。该怎么办呢?
    最直接的方法就是自己实现。负数的二进制是补码表示,非正整数的原码表示,所以可以通过原码=补码取反+1重新构造二进制字符串。最终结果就是构造后的二进制字符串转换成整数,前面加上一个负号。取反操作很容易实现,+1涉及到进位操作,有点小麻烦。 能不能利用JDK API已有的方法,这样既能避免麻烦,还可以提高性能,毕竟JDK经过多个版本更新和N多实际项目考验,性能肯定比自己写的方法要好些。
    经过思考,我想到以下办法。 假设{X}表示为X的十进制表示
    1. 原码=补码取反+1, 则原码-1=补码取反
    2. {原码-1} = {补码取反} ==> -({原码} - 1) = - {补码取反} ==> -{原码} + 1 = - {补码取反} ==> -{原码}  = - {补码取反} - 1
    3. 由于 {一个负数} = - {原码} 所以 {一个负数} = = - {补码取反} - 1。   
    例如-3的二进制是1111 1101(假设共8位), 求反得到0000 0010(2), 加上负号得到-2, 再减去1,得-3.
    所以上述结论是成立的。{补码取反}刚好可以通过Integer.valueOf(String, 2)计算。剩下要做的工作就只是对字符串取反了。
    根据以上思路,我修改了Java代码,并进行简单测试。
import java.util.Arrays;

/**
 * 二进制和数字之间相互转换类
 * <pre>
 * email: yangzhengzheng1985@163.com
 * time: 2011/8/18 8:30
 * </pre>
 * @author 杨争争
 *
 */
public class BinaryCharArrayNumberTransfer {
	/**
	 * 转换整型为二进制char数组
	 * @param number	给定整数
	 * @param dimensionOfCharArray	二进制数组的位数
	 * @return	number的二进制组成的char数组
	 */
	public static char[] transferNumber2CharArray(int number,
			int dimensionOfCharArray) {
		char[] src = Integer.toBinaryString(number).toCharArray();
		char[] dest = new char[dimensionOfCharArray];
		System.arraycopy(src, 0, dest, dimensionOfCharArray - src.length,
				src.length);
		for (int i = 0; i < 32 - src.length; i++)
			dest[i] = '0';
		return dest;
	}

	/**
	 * 二进制char数组转换为整数
	 * @param ch	二进制数组
	 * @return	二进制表示的整数
	 */
	public static int transferCharArray2Number(char[] ch) {
		if (ch.length != 32)
			throw new RuntimeException("数组大小必须为32位");

		String s = null;
		// 正数
		if (ch[0] == '0') {
			s = new String(ch);
			return Integer.valueOf(s, 2);
		} else {
			for (int i = 0; i < 32; i++) {
				if (ch[i] == '0')
					ch[i] = '1';
				else
					ch[i] = '0';
			}
			s = "-" + new String(ch);
			// 结果修正
			return Integer.valueOf(s, 2) - 1;
		}
		
	}

	// for test
	public static void main(String[] args) {
		for (int i = -10; i <= 10; i++) {
			char[] ch = BinaryCharArrayNumberTransfer.transferNumber2CharArray(
					i, 32);
			System.out.println("十进制转换二进制: " + i + "-->" + Arrays.toString(ch));
			int num = BinaryCharArrayNumberTransfer
					.transferCharArray2Number(ch);
			System.out.println("二进制转换十进制: " + num);
			System.out.println("-----------");
		}
	}
}
    运行结果如下:
    十进制转换二进制: -10-->[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0]
    二进制转换十进制: -10
    -----------
    十进制转换二进制: -9-->[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1]
    二进制转换十进制: -9
    -----------
    十进制转换二进制: -8-->[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0]
    二进制转换十进制: -8
    -----------
    十进制转换二进制: -7-->[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1]
    二进制转换十进制: -7
    ----------- 
    十进制转换二进制: -6-->[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0]
    二进制转换十进制: -6
    -----------
    十进制转换二进制: -5-->[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1]
    二进制转换十进制: -5
    ----------- 
    十进制转换二进制: -4-->[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0]
    二进制转换十进制: -4 
    -----------
    十进制转换二进制: -3-->[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1]
    二进制转换十进制: -3
    -----------
    十进制转换二进制: -2-->[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0]
    二进制转换十进制: -2
    -----------
    十进制转换二进制: -1-->[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
    二进制转换十进制: -1
    -----------
    十进制转换二进制: 0-->[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
    二进制转换十进制: 0
    -----------
    十进制转换二进制: 1-->[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]
    二进制转换十进制: 1
    -----------
    十进制转换二进制: 2-->[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0]
    二进制转换十进制: 2
    -----------
    十进制转换二进制: 3-->[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1]
    二进制转换十进制: 3
    -----------
    十进制转换二进制: 4-->[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0]
    二进制转换十进制: 4
    -----------
    十进制转换二进制: 5-->[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1]
    二进制转换十进制: 5
    -----------
    十进制转换二进制: 6-->[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0]
    二进制转换十进制: 6
    -----------
    十进制转换二进制: 7-->[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1]
    二进制转换十进制: 7
    -----------
    十进制转换二进制: 8-->[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0]
    二进制转换十进制: 8
    -----------
    十进制转换二进制: 9-->[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1]
    二进制转换十进制: 9
    -----------
    十进制转换二进制: 10-->[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0]
    二进制转换十进制: 10
    -----------

    从程序运行结果看,问题得到解决。


  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值