3.9 高精度整数——三、进制转换

3.9 高精度整数——三、进制转换

三、进制转换
1、题目和要求

时间限制:1s,内存限制:32MB,特殊判题:否
在这里插入图片描述

2、总结

存储数据选择的方式不对,做了很长时间,到最后也没有求解出来。
例程用到了重载运算符,简化计算。

//用一个普通整数初始化高精度整数
void set(int x){
	do{
		digit[size++] = x%10000;
		x /= 10000;
	}while(x!=0)
}

1)字符串与数字连接,使用了<sstream>头文件,具体使用方法参见:c++ 拼接字符串和数字。这里为什么不使用字符串直接相加(str_shang += tmp_c/N + '0')的方式?因为,求出来的商,可能是两位以上的数字

2)这里有一个非常重要的地方:ss << str_shang <<setfill(‘0’)<<setw(4)<< tmp_c/N;
比如,十进制数1,1112,0011转换为八进制数647 707 213‬(为了方便观察,逗号的标记和常用方式不一样)‬‬,求八进制中的3时,运算过程是这样的:

  1. 第一组:1除以8商为0,余数为1。由于判断条件if(tmp_c/N != 0),所以str_shang的值没有改变,为空;
  2. 第二组:由于tmp_c = numM.digit[i] + tmp_y*10000;所以现在的被除数是1,1112。1,1112除以8商为1389,余数为0。此时str_shang为1389;
  3. 第三组:0011除以8商为1,余数为3。如果不加ss << str_shang <<setfill('0')<<setw(4)<< tmp_c/N;这句话,那么str_shang为1,但实际因为前面几位除以8都为0,且0不做开头,所以商的真实值里,不应该缺0。

在这里插入图片描述


或许可以把第73行的if(tmp_c/N != 0)注释掉,但是,在不改动其它部分的情况下,不可以!去掉的话,如果tmp_c/N为0时,str_shang中的值会是四个0,而循环的判断条件while(str_shang != "")就永远无法成立,陷入死循环。

当然,对程序其他部分做做改动,去掉这个也是可以的。

这个好像跟上面没有关系,既然写上了,舍不得删了……


3)这道题采用第一种代码中将字符串从后往前,转换为四位一组的方式有很大弊端。对于其他的进制转换,这种方式求解不成立! 详解见下方:


16进制数(如:11,ABCD)需要存储时

  1. 首先需要计算出后四位的结果:13×1+12×16+11×16×16+10×16×16×16=43,981
  2. 由于进制转换时都是将其他进制转换为10进制,然后再将10进制转换为其他进制。所以,四位十进制数,最大是9999。43,981对10000取余,就是需要保存的值(3,981),43,981除以10000,就是需要进位的值(4)。
  3. 接下来,先按照十进制数存储时的方法进行存储,之后解释一下发生了什么错误。
  4. 计算接下来两位的十进制结果:1×1+1×16+4=21(这里的4是上一步的进位)。所以,下一个digit中的值应为21。

但是,1×1+1×16+4这样的算法真的对吗?数学中,这两位的计算结果应该是1×16^4 + 1×16^5 = 1,114,1124相加的应该是111而不是1×1+1×16=17并且!!!1111后面还有数字,应该和刚才求得的43,981中的3,981进一步求和。如此进行下去,如果还有16^10、16^20、16^100,很有可能会超过数据类型所能计算的范围,因此这种做法在除了十进制以外的其他进制中不能适用

这样的做法在十进制中是成立的,是因为10的次方的后几位永远是零,相加对原来的数据没有影响。


3、思路

1)由于输入输出时可能有大写字母出现,所以采用字符串方式存储。

2)可能有大数据,所以用自定义BigInteger数据类型存储输入字符串转换后的数据。

3)下面的算法实现了十进制转其他进制,主要思路是:

  1. 使用string类型num存储输入的数据
  2. 使用void str2BigInteger(string str, int weight, BigInteger &b)函数将string类型数据从后往前,每四位转换为BigInteger.digit[]中的一项。(函数的主要工作流程是:取str[i],将字符类型变为整型;乘以该位的权重,与当前已有的数据相加;更新权重。当已经凑够四位数字已经到达最开始的1、2、3位数字后,需要把相加的结果存起来:注意向前进位)。
  3. 重头戏,进制转换!!!进行这里之前需要先回想一下数学中是如何进行进制转换的:所有位的数据均参与除法运算,得到的余数是转换后的结果,注意求解顺序和最后结果顺序是相反的
  4. 明确如何计算以后,参照do-while循环里面的注释,就不难理解了。
4、代码

自己写的拙劣的代码,只能完成一部分功能。

#include <iostream>
#include <iomanip> 
#include <string>
#include <sstream>
using namespace std;

struct BigInteger{
	int size;
	int digit[1000];
	
	BigInteger(){
		size = 0;
	}
}numM;

void str2BigInteger(string str, int weight, BigInteger &b){
	int c = 1,num = 0,i,carry = 0;
	int tmp;
	//从最后一位开始,从后往前存在digit[]的从前往后大小内 
	for(i = str.size()-1; i>=0; i--){
		//由于str中可能出现字母,所以需要换成相应的十进制数 
		tmp = (str[i] >= 'A') ? (str[i] - 'A' + 10) : (str[i] - '0');
		num += tmp * c;
		c *= weight;
		
		if(c > weight*weight*weight || i == 0){
			if(numM.size != 0){
				num = (num*weight*weight*weight*weight + carry * 10000)/10000;
			}
			carry = num/10000;
			numM.digit[numM.size++] = num%10000;
			c = 1;
			num = 0;
		}
	}
}

void printBigInteger(BigInteger b){
		for(int i = b.size-1; i>=0; i--){
			if(i == b.size-1){
				cout<<b.digit[i];
			}else{
				cout<<setfill('0')<<setw(4)<<b.digit[i];
			}
		}
	cout<<endl;
}

//M,N,num 代表题中输入的数据
//tmp_y,tmp_c 代表每一个digit[i]求得的余数和商 
//output,str_shang 代表存到进制转换后的字符串、整个数的商 
int main(){
	int M=10,N=8,tmp_y=0,tmp_c,tmp;
	string num="11112001111",output = "",str_shang="";
	//cin>>M>>N>>num;
	//cin>>num;
	
	str2BigInteger(num, M, numM);
	printBigInteger(numM);
	do{
		str_shang = "";
		//要想求得整个数的商,需要把整个数组遍历一遍
		//实际数据:ABCDEFG;digit[]:DEFG ABC ,因此从后往前遍历digit[]以求得商和余数。
		//商需要按照 str2BigInteger()方法再存入digit[]
		//余数作为最后结果,需要按F的值进行转换(F>10时,11->A),存入output中
		// 余数的求解顺序,先求的最后一位,从后往前逐个求解,保存在output中,所以输出需要倒序输出output中内容
		for(int i = numM.size-1; i>=0; i--){
			//求商时:
			//numM.digit[i]需要加上 numM.digit[i-1]的余数,再继续进行运算 
			tmp_c = numM.digit[i] + tmp_y*10000;
			tmp_y = tmp_c % N;
			//最小是五位数,五位数除以两位数,肯定不为0 
			if(tmp_c/N != 0){
				stringstream ss;
    			ss << str_shang <<setfill('0')<<setw(4)<< tmp_c/N;
    			str_shang = ss.str();
			}
		}
		//求余数时:
		//需要按F的值进行转换
		output += (tmp_y >= 10) ? (tmp_y - 10 + 'A') : (tmp_y + '0');

		if(str_shang != ""){
			numM.size = 0;
			str2BigInteger(str_shang, M, numM);
			tmp_y=0;
		}

	}while(str_shang != "");
	for(int i = output.size()-1; i>=0; i--){
		cout<<output[i];
	}
	cout<<endl; 
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值