【文章标题】从计算机的核心思想探讨一种进制转换的方法
【文章作者】曾健生
【作者邮箱】[email protected]
【作者QQ】190678908
【作者博客】http://blog.csdn.net/newjueqi
【编程环境】JDK 1.6.0_01
【作者声明】欢迎转载文章,但转载请保留文章的完整性以及注明文章的出处。
*******************************************************************************
在本人写的博客文章《一种进制转换的特殊方法》(http://blog.csdn.net/newjueqi/archive/2009/04/05/4049468.aspx)本人从“计算机上所有数据都是二进制数据”这个角度探讨了一种进制转换的方法,但由于这个思想比较难理解,而且举的例子也有一定的难度,所以写了本文作为补充,举了一个简单的八进制转换为16进制的算法(因为本人认为这个例子比较容易理解想要阐述的思想)。如果《一种进制转换的特殊方法》(http://blog.csdn.net/newjueqi/archive/2009/04/05/4049468.aspx)一文看得不太明白,可先阅读本文。
大家知道,计算机是基于二进制的,所有数据都是以0和1的方式存放的,我们在屏幕上看到的丰富多彩的数据(包括各种声音,图像等)在计算机上都是一大串0和1,同样的道理,我们在屏幕上看到的以各种进制表示的数据在硬盘和内存中都是以二进制的形式存放的。换句话来说,无论我们怎样转换进制,但最后存储在电脑上的都是二进制数,编程实现的进制转换只是改变数据的显示形式,但对在硬盘或内存中的存储的实际数据没有丝毫改变,因为数据在硬盘中永远只有0和1。
虽然以上的一段话讲的都是一些很闲浅的知识点,只要有一点计算机基础的同学都知道,但只有深刻理解上面一段话,才能弄明白本文所阐述的算法思想。
下面开始阐述这个进制转换的思想:
因为所有数据在计算机中都是以二进制的形式存放的,所以用编程实现的进制转换本质是把二进制数据用不同的进制形式显示出来,切记,进制转换只是改变数据的显示形式,在计算机中存放的依然是原来的二进制数据。
下面举一个例子,譬如有以下的语句:
int num=045;
这个语句的作用是把八进制值045赋予给变量num,虽然我们输入的八进制值045,但在计算机中存储的数据实际上是100101,为什么是100101呢?因为在计算中所有的数据都是以二进制的方式存储的,无论我们输入的值是十进制,八进制或者16进制,在计算机中存储的都是二进制数据。
既然各种进制的数据在计算机中都是以二进制的形式存储的,那么在转换进制时就能得到一个等效的进制转换思想:在计算机中所有的M进制转N进制,都等效为二进制转N进制。为什么这样?因为所有的M进制在计算机上都是以二进制的形式存储。
所以用上一篇博文《一种进制转换的特殊方法》(http://blog.csdn.net/newjueqi/archive/2009/04/05/4049468.aspx)中的程序1(把8进制转16进制)实现进制转换,如果想修改为十进制转16进制,只需要把输入的数据改为十进制的形式,算法根本不需要修改,究其原因是算法的原理是把输入数据转换为二进制数据看待的。
用位操作实现进制转换的算法能同时适用于多种进制的原因是位操作能舍弃各种进制的外相(譬如十进制,八进制或者16进制的表现形式),直指数据在计算机中的本质:二进制,所以针对二进制数据的操作就能适用于多种进制。
下面详细分析一段程序代码例子,这段代码的作用是把八进制数据067转换为10进制数据显示出来,这个算法就是把八进制数据转换为二进制数据处理
//程序2:EightHexToTen.java
/**
*程序用途:通过移位实现八进制转换为10进制
*作者:曾健生
*日期:2009-4-6
*个人博客: http://blog.csdn.net/newjueqi
*/
class EightHexToTen
{
public static void main(String args[])
{
int num=067; //输入八进制数据067
int temp=0; //存储临时的数据
int multiple=1; //累积的倍数
int sum=0; //存储最终的结果
while( num>0 )
{
temp=num&1; //语句①,把二进制位取出来
sum=sum+multiple*temp; //语句②,把这位所表示的十进制数累加
multiple=multiple<<1; //语句③,累加的数*2
num=num>>1; //语句④,把下一位移到第一位的位置上
}
System.out.println(sum);
}
}
输出结果为55
算法的过程如下:
(1) 当执行完语句int num=067 后,num的值为067,在计算机中实际存储的数据是110111(由于num是int型数据占据32位,实际110111前还有一大串0,但是为了阅读的方便,把110111前的0省略掉)
(2) 要把八进制数据转换为二进制数据处理,就必须用到位运算,而二进制转换为十进制的方法是把二进制中的每一位的数字乘以2的N次方(N为二进制数据的第M位减1)累积相加。那现在的问题是怎么把二进制中需要的位提出来?在逻辑与(&)运算中,任何数和1进行与(&)运算都是它的本身,任何数和0进行与(&)运算都变为0,现在程序开始执行要把二进制数据110111的第一位提出来,换个角度就是把第一位置1,其它位置0,所以需要把二进制数据110111和000001进行逻辑与(&),也就是语句①(temp=num&1),执行完这句后temp存储的值就是110111中的第一位1。
(3) 语句②(sum=sum+multiple*temp)是根据二进制转换为十进制的方法,把二进制中的每一位的数字乘以2的N次方(N为二进制数据的第M位减1)累积相加。先把第一位的值1乘以这个位对应的2的0次方的值(即multiple的初始值1),把结果存储在一个累积变量。
(4) 由于下一次执行语句②时multiple的值必须变为2的1次方,所以把multiple的值*2,也就是语句③(multiple=multiple<<1)
(5) 现在已经处理完二进制数据110111的第一位,那么下次进入while循环后就必须处理110111的第二位,这时又有一个新的问题:由于需要把110111的第二位提出来,就需要用二进制数10和110111进行与运算(&),那么第三次,第四次呢?如果这样就意味着每提110111中的一位出来,都需要构造一个新的数和110111进行与运算(&),这是多么麻烦的一件事情!!!所以我们需要移位运算的帮助,在进入下一次while循环前把num的值右移一位,就不需要每次都构造新的数字和110111进行与运算(&),用了语句④(num=num>>1)后每次进入新的循环就只需执行语句①(temp=num&1)就能把需要的位数提出来。
(6) 不断地重复(2)(3)(4)(5)的过程,直到num的值等于0,那就表示所有位都已经处理完毕,结束循环,输出结果。
如果现在不想用八进制转10进制,想玩玩别的进制转换,想把16进制转10进制,那也很简单,只需要把语句int num=067中的值改为16进制数。
我们可以试验一下把16进制数0xA4转换为10进制,把语句int num=067改为int num=0xA4,编译,运行程序,得到结果如下图:
不需要修改算法就能把原来的八进制转10进制变为16进制转10进制的根本原因是由于我们的进制转换算法是针对计算机中数据的核心:二进制数据,所进行的,不需要理会数据的外相(十进制,八进制或者16进制)。
最后总结一下:
在计算机中所有的数据都是以二进制的形式存储的(无论是十进制,八进制或者16进制),所以在进制转换的过程中,如果我们不理会进制的外相(即十进制,八进制或者16进制等表现形式)而直指数据在计算及中的核心:二进制,就能方便的实现进制的转换。
附:在上篇博客文章《一种进制转换的特殊方法》(http://blog.csdn.net/newjueqi/archive/2009/04/05/4049468.aspx)末给了个问题:用移位的方法实现10进制转8进制,在这里贴一份参考代码
/**
*程序名称:TenToOct.java
*程序用途:移位的方法实现10进制转8进制
*作者:曾健生
*日期:2009-4-6
*个人博客: http://blog.csdn.net/newjueqi
*状态:调试通过
*/
class TenToOct
{
public static void main(String args[])
{
int num=98; //输入的十进制数据
int temp=0; //临时变量
StringBuffer sb=new StringBuffer();
while( num>0 )
{
temp=num&7; //提出需要的位
sb.append(temp);
num=num>>3; //由于是转化为8进制,所以需要移3位
}
sb.reverse();
System.out.println(sb);
}
}