您的位置:首页 > 编程语言 > Java开发

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

2011-08-19 13:48 507 查看
 
  我的一个项目需要经常用到二进制和十进制整数之间的相互转换。这应该是个非常容易实现的问题,所用到的算法也极其简单。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数组
*/
publi
112b5
c 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

    -----------

    从程序运行结果看,问题得到解决。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息