C语言实现进制转换(附带源码)

这是一个C语言示例:输入一个 M 进制的整数 x,实现对 x 向任意非 M 进制的数的转换。

掌握不同数制之间的转换关系是解决该问题的关键:
  • 十进制转换为二进制、八进制、十六进制:整数部分除以基数(二进制基数为 2,八进制基数为 8,十六进制基数为 16)取余数(取余数方向为从后向前),小数部分乘以基数取整数(取整方向为从前向后)。
  • 二进制、八进制、十六进制转换为十进制:按权值展开相加。
  • 二进制、八进制、十六进制的相互转换:先转换为十进制,再转换成其他进制。

从各种数制转换方法可以看出,数制间转换时都是以各个数位上的数字参加某种运算的。因而,我们设计一个数组 temp[ ] 用来存放转换前后的数,则这个数各位上的数字分别就是一个数组元素。比如,将十六进制数“25D”存入 temp[ ] 数组后,temp[0] 的值为字符'2',temp[1] 的值为字符'5',temp[2] 的值为字符'D'。

但是,在数制转换中参与运算的各数位上的数字其类型都是整型而非字符型,因而,我们还需要设计两个函数,一个用于将字符转换成数字,另一个用于将数字转换为字符。

在该问题中,字符转换为对应的数字分两类情况考虑:
  1. 第一类是介于'0'到'9'之间的字符,转换成相应的数字 0~9 时,可利用其 ASCII 码之间的对应关系。字符'0'的 ASCII 码是 48,字符'1'的 ASCII 码是 49,则运算式'1'-'0'=1,得到的这个差即为字符'1'对应的数字 1,同理,'2'-'0'=2,即可得到字符'2'对应的数字 2。由此可知,介于'0'到'9'之间的字符 ch 对应的数字 num 可以用公式num=ch-'0'得到。
  2. 第二类是介于'A'到'F'之间的字符,字符'A'对应的数字是 10,字符’B’对应的数字是 11,对于此类字符可以利用公式 num=ch-'A'+10 得到对应的数字。

int char_to_num(char ch)              //自定义函数:返回字符对应的数字
{
    if(ch>='0'&&ch<='9')
        return ch-'0';                //将数字字符转换成数字
    else
        return ch-'A'+10;             //将字母字符转换成数字
}

同理,数字转换为对应的字符也分两种情况:
  1. 一种是 0~9 之间的数字,只需用字符'0'的 ASCII 码 48 加上相应的数值,然后进行强制类型转换将其转换为字符型即可,具体公式为 (char)('0'+num-0)。
  2. 另一种 10~15 之间的数值,同样用字符'A'的 ASCII 码 65 加上相应的数值再减去 10,然后进行强制类型转换将其转换为字符型即可,公式为 (char)('A'+num-10)。

char num_to_char(int num)         //自定义函数:返回数字对应的字符
{
    if(num>=0&&num<=9)
        return (char)('0'+num-0);       //将 0~9 转换成字符
    else
        return (char)('A'+num-10)       //将 10~15 转换成字符
}

其他数值转换成十进制采用按权展开相加的方法,因此我们定义一个变量 decimal_num 来存储相加的和,同时定义一个变量 decimal_s 来计算并存储各位上的权值。以下为其他数值转换为十进制数的自定义函数代码:

long source_to_decimal(char temp[],int source)
{
    long decimal_num=0;                //存储按权展开累加所得的和(即十进制数)
    long decimal_s;                    //存储个位上的权值
    int i, j, length;           
    for(i=0;temp[i]!='\0';i++);        //计算有效字符个数
    length = i;
    for(i=0;i<length-1;i++)
    {
        decimal_s=1;
        for(j=1;j<=length-i-1;j++)     //计算各位上的权值
            decimal_s *= source;
        decimal_num += decimal_s*char_to_num(temp[i]);      //累加
    }
    return decimal_num;                //返回由其他数制转换成的十进制数
}

以上代码中使用了双重 for 循环语句,程序运行效率不高。其实我们可以将各位权值的计算过程合并在展开累加这层循环当中,这样就可以减少一层 for 循环,提高程序运行效率。优化以后的函数代码如下:
long source_to_decimal(char temp[ ],int source)
{
    long decimal_num=0;             //存储展开之后的和
    int i, j;
    for(i=0;temp[i]!='\0';i++);     //计算数组中有效字符数
    for(j=0; j<=i-1; j++)           //累加
        decimal_num=(decimal_num*source)+char_to_num(temp[j]);
    return decimal_num;             //返回由其他数制转换成的十进制数
}

上面的代码中 decimal_num 在每次累加时先要乘一次基数,这样累加 n 次将所有的有效元素都转换累加后,第一个元素 temp[0] 与基数相乘了 n-1 次,即基数的 n-1 次方,刚好是其权值,第二个元素 temp[1] 与基数相乘了 n-2 次,即基数的 n-2 次方,也刚好是其权值,……最后一个元素 temp[n-1] 与基数相乘了 0 次,即基数的 0 次方,也是其权值 1。

十进制转换成其他数制采用除以基数取余的方法。以十进制转化为八进制为例:
  1. 首先用当前的十进制数 decimal_num 除以基数 8(object),得到的余数转化为字符(调用 num_to_char( ) 函数)并存放在数组元素 temp[0] 中,将相除之后的商再次赋值给 decimal__num;
  2. 继续用得到的新十进制数 decimal_num 除以基数 8,再将得到的余数转化为字符并存放在数组元素 temp[1] 中,一直重复上述过程直到十进制数 decimal_num 为 0。
  3. 将得到最后一个余数存入数组元素 temp[i-1] 中之后,在下一个数组元素 temp[i] 中存入一个字符串结束符\0

十进制转换成其他数制并打印转换后的新数的自定义函数代码如下:
void decimal_to_obj(char temp[ ],long decimal_num,int object)
{
    int i=0;
    while(decimal_num)
    {
        temp[i]=num_to_char(decimal_num%object);     //求余数并转为字符
        decimal_num=decimal_num/object;              //用十进制数除以基数
        i++;
    }
    temp[i]='\0';
    int j;
    for(j=i-1;j>=0;j--)                              //倒序输出temp数组(由十进制数转换成的新数)
        printf("%c",temp[j]);
    printf("\n");
}

代码清单 1:数制转换
#include <stdio.h>
#include <stdlib.h>
#define MAXCHAR 101             /*最大允许字符串长度*/  
int char_to_num(char ch)  
{ 
    if(ch>='0'&&ch<='9')  
        return ch-'0';         /*将数字字符转换成数字*/
    else     
       return ch-'A'+10;        /*将字母字符转换成数字*/
}  
char num_to_char(int num)  
{ 
    if(num>=0&&num<=9)  
        return (char)('0'+num-0);      /*将0~9之间的数字转换成字符*/
    else 
        return (char)('A'+num-10);      /*将大于10的数字转换成字符*/
}  
long source_to_decimal(char temp[],int source)  
{ 
    long decimal_num=0;        /*存储展开之后的和*/
    int length;  
    int i;  
    for(i=0;temp[i]!='\0';i++);  
        length=i;  
    for(i=0;i<=length-1;i++)        /*累加*/
        decimal_num=(decimal_num*source)+char_to_num(temp[i]);  
    return decimal_num;  
}  
void decimal_to_object(char temp[],long decimal_num,int object)  
{ 
    int i=0;  
    while(decimal_num)  
    { 
        temp[i]=num_to_char(decimal_num%object);   /*求出余数并转换为字符*/
        decimal_num=decimal_num/object;    /*用十进制数除以基数*/
        i++;     
    }  
    temp[i]='\0';
    int j;  
    for(j=i-1;j>=0;j--)        /*输出temp数组中的值*/
        printf("%c",temp[j]);  
    printf("\n"); 
}  
int main()  
{  
    int source;          /*存储原数制*/
    int object;           /*存储目标数制*/     
    int length;           /*存储转换成目标数制后字符数组的长度*/ 
    long decimal_num;         /*存储转换成的10进制数*/ 
    char temp[MAXCHAR];       /*存储待转换的数值和转换后的数值*/ 
    int flag=1;           /*存储是否退出程序的标志*/ 
    while(flag)          /*利用输入的flag值控制循环是否结束*/
    { 
        printf("转换前的数是:");   scanf("%s",temp);  
        printf("转换前的数制是:"); scanf("%d",&source);  
        printf("转换后的数制是:"); scanf("%d",&object);  
        printf("转换后的数是:");  
        decimal_num = source_to_decimal(temp,source);  
        decimal_to_object(temp,decimal_num,object);
        printf("\n继续请输入1,否则输入0:\n");  
        scanf("%d",&flag);  
    }  
    system("pause");
    return 0;
}

运行结果为:

转换前的数是:17
转换前的数制是:10
转换后的数制是:2
转换后的数是:10001

继续请输入1,否则输入0:
1
转换前的数是:AF7
转换前的数制是:16
转换后的数制是:10
转换后的数是:2807

继续请输入1,否则输入0:
1
转换前的数是:653
转换前的数制是:8
转换后的数制是:16
转换后的数是:1AB

继续请输入1,否则输入0:
1
转换前的数是:100010011100
转换前的数制是:2
转换后的数制是:8
转换后的数是:4234

继续请输入1,否则输入0:
0