基于C语言下的任意进制转换程序(适配所有情况包括小数负数)

        比较好理解的任意进制转换程序(适配2~32(其实36也行,因为有26个英文字母)进制),由于不同进制转换往往出现无限循环小数,本程序设定当结果与真实值误差小于0.000001时认定为基本完成.

        详细程序思路解释已经附在代码中,有任何更好的想法或者问题欢迎在评论区与我交流

        头文件:

#pragma once
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
/*对于M进制的数x的十进制以及任意进制转换,使用数字数组会导致在输入符号或者小数点时出现麻烦,并且牵扯到高于十进制时的数字表达问题时,无法使用abcd表示10,11等数字,所以先敲定使用字符类型数据用于基本的键入数字的保存与大于十进制数的保存
* 其次,对于所有的进制转换,都需要暂时将其转换为十进制,因为程序设计的计算模块是基于十进制的,如果是两个非十进制的数相加,则将两者都转化为十进制后再进行运算,之后再转化为原本进制。
* 对于程序要求,则首先是十进制转化函数,可以直接使用数字类型float作为相关返回值,因为其需要用于运算,而其他进制转换函数则使用字符数组进行存储。对于运算过程中的数字,这里建议使用结构体来进行相应的数据存储,因为其可以同时调用出相当多
* 的与进制转换有用的信息,例如小数点位置,长度,进制。
* 最终结果可能与原数存在略微差异,因为大多数数值转换后会出现大量无限循环小数,只能进行有限的取舍,最终结果较为逼近真实值
*/
typedef struct shushujv{
	char* zifubiaoda;
	int changdu;
	int jinzhi;
	int xiaoshudianweizhi;
}*ssjdizhi,ssjshiti;
ssjdizhi yonghushuru();											//负责用户输入一个确定进制和值的数字
void jvtifuzhi(ssjdizhi fanhui);		//将用户输入的字符类型数据存入数组中
int zhaoxiaoshudian(ssjdizhi fanhui);	//找小数点在字符串中的位置
float shijinzhizhuanhuan(ssjdizhi shu);	//这一函数包含了前三个函数,十进制转换
int cifang(int shu, int ci);			//求某个数的x次方
ssjdizhi xjinzhizhuanhuan(int mubiaojinzhi, ssjdizhi shu);//任意进制转换,包含了上述所有函数

主要函数体:

#include"yao.h"
ssjdizhi yonghushuru() {
	ssjdizhi fanhui = (ssjdizhi)malloc(sizeof(ssjshiti));
	fanhui->changdu = 1;
	fanhui->zifubiaoda = (char*)malloc(sizeof(char) * fanhui->changdu);
	printf("请输入进制\n");
	scanf_s("%d", &(fanhui->jinzhi));
	getchar();
	printf("请输入数字\n");
	jvtifuzhi(fanhui);
	fanhui->xiaoshudianweizhi = zhaoxiaoshudian(fanhui);
	return fanhui;
}
void jvtifuzhi(ssjdizhi fanhui) {
	char zifu;
	while (1) {
		zifu = getchar();
		if (zifu == '\n') {//使用getchar读取回车后不需要利用一个新的getchar清空缓冲区
			break;
		}
		fanhui->zifubiaoda[fanhui->changdu - 1] = zifu;
		fanhui->changdu++;
		fanhui->zifubiaoda = (char*)realloc(fanhui->zifubiaoda,sizeof(char) * fanhui->changdu);
	}
	fanhui->changdu--;
	fanhui->zifubiaoda[fanhui->changdu] = '\0';
}
int zhaoxiaoshudian(ssjdizhi fanhui) {
	for (int a = 0; a < fanhui->changdu; a++) {
		if (fanhui->zifubiaoda[a] == '.') {
			return a;
		}
	}
	return fanhui->changdu;
}
float shijinzhizhuanhuan(ssjdizhi shu) {
	float fanhui = 0;
	float canshu;						//参数,即对应的每一数位上代表的值,例如十进制下111.1,从左到右分别为100,10,1,0.1
	int a = 0;//用于辅助定位正在处理的数位
	if (shu->zifubiaoda[0] == '-') {
		a++;//如果为负数,那么a要定位在数值位上就要加一定位到后面的数字
		canshu = (float)cifang(shu->jinzhi, shu->xiaoshudianweizhi - 2);//同时,小数点位置反应了其前方有多少个字符,前方有x个字符则最开头一位数字的数位值为进制的x-1次方,这里由于多了一位符号所以为x-2
	}
	else {
		canshu = (float)cifang(shu->jinzhi, shu->xiaoshudianweizhi - 1);//求出最高数位值是为了后续利用其直接与每一位具体数字相乘,得出十进制下结果
	}
	for (; a < shu->xiaoshudianweizhi; a++) {					//这里开始分两部分,小数点之前和小数点之后,避免反复判断是否当前字符为小数点
		if (((int)shu->zifubiaoda[a]) < 58) {
			fanhui += ((float)((int)shu->zifubiaoda[a] - 48)) * canshu;
			canshu /= shu->jinzhi;
		}
		else {															//由于高于十进制的数使用ABCD表示,这里通过分类讨论将字符数据转化为相应的ascii值再修改为正确的整形数字,48对应0,65对应A
			fanhui += ((float)((int)shu->zifubiaoda[a] - 55)) * canshu;//返回值就是最终十进制结果,其不断加等与每一位的数位值乘以具体数字
			canshu /= shu->jinzhi;//每过一位参数值也要有相应变化
		}
	}
	for (int b = shu->xiaoshudianweizhi + 1; b < shu->changdu; b++) {
		if (((int)shu->zifubiaoda[b]) < 58) {
			fanhui += ((float)((int)shu->zifubiaoda[b] - 48)) * canshu;
			canshu /= shu->jinzhi;
		}
		else {
			fanhui += ((float)((int)shu->zifubiaoda[b] - 55)) * canshu;
			canshu /= shu->jinzhi;
		}
	}
	if (shu->zifubiaoda[0] == '-') {//由于计算时未考虑正负,如果字符数据表示当前为负数,就要最后修改为负数
		fanhui *= -1;
	}
	return fanhui;
}
int cifang(int shu, int ci) {
	int canshu = shu;//记录初始进制用于计算
	if (ci == 0) {
		return 1;
	}
	for (ci; ci > 1; ci--) {
		shu *= canshu;
	}
	return shu;
}
ssjdizhi xjinzhizhuanhuan(int mubiaojinzhi, ssjdizhi shu) {
	//在计算机里,任何非十进制的两个进制互相转化,都需要先转变为十进制,所以先使用上述函数得到原进制数的十进制表达,再由十进制转化为目标进制
	float shijinzhi = shijinzhizhuanhuan(shu);
	ssjdizhi fanhui = (ssjdizhi)malloc(sizeof(ssjshiti));//先定义出返回的结构体
	fanhui->jinzhi = mubiaojinzhi;
	if (shijinzhi > 0) {
		fanhui->changdu = 1;
		fanhui->zifubiaoda = (char*)malloc(sizeof(char) * fanhui->changdu);
	}
	else {
		fanhui->changdu = 2;
		fanhui->zifubiaoda = (char*)malloc(sizeof(char) * fanhui->changdu);
		fanhui->zifubiaoda[0] = '-';
		shijinzhi *= -1;
	}
	//与上述判断正负类似,如果发现要转变的数是负数,就要额外先申请一块存储负号的空间并将长度定义为2
	float canshu = 1;
	while (1) {
		if (canshu > shijinzhi) {
			break;
		}
		canshu *= mubiaojinzhi;
	}
	canshu /= mubiaojinzhi;
	int shang;
	//这里是得出一个目标进制下,比十进制数总值小的最大的数位值,用于从首位开始得出后面每个数位上的具体数字,具体操作类似于一开始求十进制数的反向操作,将总值除以当前数位值得出当前数位具体数字,再用
	//总值减去当前数位与其具体数字的乘积,算出剩余的值,投入下一数位使用,设定最终总值小于0.000001就认为当前数已经接近真实值
	while (1) {
		shang = (int)(shijinzhi / canshu);//这里需要注意float转化为int类型,会直接取整数部分
		if (shang < 10) {					//同样的,如果大于9,则需要使用ABCD表示法,也就是反演,将int数据加上48转化为o~9的字符,加上55转化为A类的字符
			fanhui->zifubiaoda[fanhui->changdu - 1] = (char)(shang + 48);
		}
		else {
			fanhui->zifubiaoda[fanhui->changdu - 1] = (char)(shang + 55);//fanhui->changdu始终指向当前数组空缺的最后一位,再填入完成后
		}
		fanhui->changdu++;												//及时增加长度,使用realloc扩充空间,更新十进制总值,以及数位值
		fanhui->zifubiaoda = (char*)realloc(fanhui->zifubiaoda, fanhui->changdu);	
		shijinzhi -= shang * canshu;
		canshu /= fanhui->jinzhi;
		if (shijinzhi < 1&&canshu<1) {	//这里的判断条件很重要,有可能在到达个位之前,总值就已经小于1,可能前方位数能够整除数位值参数,导致后续数位值都是0,所以必须确保字符填充一直进行完毕至最后一位,参数
			if (shijinzhi > 0.000001) {	//如果十进制总值还有剩余,也就是没有被某一位小数点之前的数整除,或者比规定的精度0.000001要大,还有相关的比较价值再去添加小数点来为小数位赋值
				fanhui->zifubiaoda[fanhui->changdu - 1] = '.';	//此时应当已经更新至1/jinzhi状态,必定小于1,当为个位赋值后,此时总值必定小于1,我们之前已经更新过总值和数位值,所以这里只增加空间填充一个小数点
				fanhui->changdu++;
				fanhui->zifubiaoda = (char*)realloc(fanhui->zifubiaoda, fanhui->changdu);
			}
			break;
		}
	}
	while (1) {
		if (shijinzhi < 0.000001) {//这里同上,一直到总值小于0.000001
			break;
		}
		shang = (int)(shijinzhi/canshu);
		if (shang < 10) {
			fanhui->zifubiaoda[fanhui->changdu - 1] = (char)(shang + 48);
		}
		else {
			fanhui->zifubiaoda[fanhui->changdu - 1] = (char)(shang + 55);
		}
		fanhui->changdu++;																
		fanhui->zifubiaoda = (char*)realloc(fanhui->zifubiaoda, fanhui->changdu);
		shijinzhi -= shang * canshu;
		canshu /= fanhui->jinzhi;
	}
	fanhui->zifubiaoda[--fanhui->changdu] = '\0';										//经典的善后工作,名义上长度-1,但实际上用多出来的空间存储空字符,对于整数来说,小数点与空字符位置一致
	fanhui->xiaoshudianweizhi = zhaoxiaoshudian(fanhui);
	return fanhui;
}

 运行主函数:

#include"yao.h"
void main() {
/*使用方法:
请输入进制
11
请输入数字
-A1.1
十进制下该数字为-111.090912
请输入目标进制
11
请输入进制
10
请输入数字
-111.090912
11进制下,该数字为-A1.0AAAA2
*/
	printf("十进制下该数字为%f\n",shijinzhizhuanhuan(yonghushuru()));
	printf("请输入目标进制\n");
	int mubiaojinzhi;
	scanf_s("%d", &mubiaojinzhi);
	ssjdizhi fanhui = xjinzhizhuanhuan(mubiaojinzhi, yonghushuru());
	printf("%d进制下,该数字为", mubiaojinzhi);
	puts(fanhui->zifubiaoda);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值