巨大数(C语言)

何为巨大数呢?顾名思义,巨大数就是数值特别大的数字,大到C语言已经无法识别,例如几千万亿的加减乘除,C语言的int, 与long int显然已经无法表示,因为他们都是四字节的补码,可以表示的整型数字范围为-2,147,483,648 ~ 2,147,483,647, 即-2^32 ~ 2^32-1。
那么该如何解决这个问题呢?
首先,根据int的特性我们可以使用万进制,类似于十进制,只不过万进制是4位一组,十进制是一位一组。说到这可能就会有人疑惑了,为啥是万进制,不是十万进制,或者千进制呢,偏偏选一个万进制。因为如果我们在进行乘法运算时,每一位最多是9999两者相乘的话既不会超过int的表示范围,也没有对int的表示范围造成太大的浪费。
现在我们来解决一下数字存储的问题:
我们可以将数字提前存入文件中,直接从文件调入,这样方便我们的测试,而且方便我们的修改,将数据从文件调入后接下来就是该将这些一大堆的字符串转化成万进制存入一个int型的数组里。
这里告诉大家一个小窍门,就是反着存,怎么讲呢就是巨大数的低位存入数组的前面,位越高存储的下标越靠后,这样做的好处就是在进行运算的时候很符合我们的运算习惯从低位开始,而且同时也是从数组下标为0的那一个数开始。那么我们接下来来一段代码看看:

void storeInfor(HUGE_NUM *NUM, char *temp) {
	int i = 0;
	int j = 0;
	int k = 0;
	int flag = 1;


	if (temp[0] == '-') {
		NUM->sign = 1;
		for (i = 0; i < NUM->count-1; i++) {
			temp[i] = temp[i+1];
		}
		NUM->count--;

	} else {
		NUM->sign = 0;

	}

	for (j = NUM->count-1, i = 0; j >= NUM->count%4 + 3; i++) {
			NUM->value[i] = ((int)temp[j]-48) + ((int)temp[j-1] - 48) * 10 + ((int)temp[j-2] - 48)* 100 + ((int)temp[j-3] - 48)* 1000;
			j = j-4;
		}
		for (k = NUM->count%4-1,flag = 1; k >= 0; k--) {
			if (k == NUM->count%4-1) {
				NUM->value[i] = ((int)temp[k]-48) * flag;
			} else {
				NUM->value[i] += ((int)temp[k]-48) * flag;
			}
			flag = flag * 10;
		}

		printInfor(NUM);
		
}

这里的有些代码有些看不懂没关系,最后我会附上所有的完整代码,供大家参考。

到这我们算是把所有数字存起来了,下来我们先看看一下基本运算-加减运算。
加法运算当然很简单,这里大家可以先试一下十进制的加法运算如何实现。
相较于加法,减法就难实现的多了,因为进位我们可以很轻松的通过给他的加一搞定,可是借位呢,如果出现连续借位呢这对我们将是一个不小的考验,怎么搞呢?
这里给大家介绍一下由我老师朱洪自己研究的一种方式-微易码补码,根据底层的原理知识,推理得到的,我们都知道计算机实现减法是通过补码来实现的,我老师的方法同样是如此。接下来给大家看一下这个神之一手:

在这里插入图片描述
现在这些主要的知识个技术都给大家说了,大家就看看我的代码吧:

#include <stdio.h>
#include <malloc.h>


typedef struct HUGE_NUM {
	int sign;
	int *value;
	int count;

}HUGE_NUM;

void storeInfor(HUGE_NUM *NUM, char *temp);
void addNum(HUGE_NUM *NUM1, HUGE_NUM *NUM2, HUGE_NUM *NUM3, char *temp1, char *temp2);
void printInfor(HUGE_NUM *NUM);

void printInfor(HUGE_NUM *NUM) {
	int i;
	int count = (NUM->count+3) / 4 - 1; 
	if (NUM->sign == 1) {
		printf("-:");
	} else {
		printf("+:");
	}
	if(NUM->value[(NUM->count+3) / 4 - 1] == 0) {
		count = (NUM->count+3) / 4 - 2;
	}
	for (i = count; i >= 0; i--) {
		if (i == count) {
			printf("%d ",NUM->value[i]);
		} else {
			printf("%04d ",NUM->value[i]);
		}
	}
	printf("\n");
}

void addNum(HUGE_NUM *NUM1, HUGE_NUM *NUM2, HUGE_NUM *NUM3, char *temp1, char *temp2) {
	int sign;
	int *caculate1;
	int *caculate2;
	int count;
	int i;

	if (NUM1->count >= NUM2->count) {
		count = (NUM1->count + 3) / 4 + 1;
		caculate1 = (int *) calloc(sizeof(int),count);
		caculate2 = (int *) calloc(sizeof(int),count);
	} else {
		count = (NUM2->count + 3) / 4 + 1;
		caculate1 = (int *) calloc(sizeof(int),count);
		caculate2 = (int *) calloc(sizeof(int),count);

	}

	for (i = 0; i < (NUM1->count+3)/4; i++) {
		caculate1[i] = NUM1->value[i];
	}
	for (i = 0; i < (NUM2->count+3)/4; i++) {
		caculate2[i] = NUM2->value[i];
	}

	if (NUM1->sign == NUM2->sign) {
		if(NUM1->sign == 0) {
			NUM3->sign = 0;
		} else {
			NUM3->sign = 1;
		}
		for (i = 0; i < count; i++) {
			if (i == 0) {
				NUM3->value[i] = (caculate1[i]+caculate2[i])%10000;	
			} else {
				NUM3->value[i] = (caculate1[i]+caculate2[i])%10000 + (caculate1[i-1]+caculate2[i-1])/10000; 
				if(NUM3->value[i-1] == 10000) {
					NUM3->value[i]++;
					NUM3->value[i-1] = 0;
				}
			}	
		}

		if(NUM3->value[count-1] >= 10000) {
			NUM3->value[count-1] -= 10000;
			NUM3->value[count]++;
		}
	} else {
		 if (NUM1->sign == 1) {

		 	for (i = 0; i < count; i++) {
		 		caculate1[i] = 9999 - caculate1[i];
		 	}

		 }
		 if (NUM2->sign == 1) {

		 	for (i = 0; i < count; i++) {
		 		caculate2[i] = 9999 - caculate2[i];
		 	}

		 }
	
		 for (i = 0; i < count; i++) {
			if (i == 0) {
				NUM3->value[i] = (caculate1[i]+caculate2[i])%10000;	
			} else {
				NUM3->value[i] = (caculate1[i]+caculate2[i])%10000 + (caculate1[i-1]+caculate2[i-1])/10000;
				if(NUM3->value[i-1] == 10000) {
					NUM3->value[i]++;
					NUM3->value[i-1] = 0;
				}
			}
			
		}
		if(NUM3->value[count-1] >= 10000) {
			NUM3->value[count-1] -= 10000;
			NUM3->value[count]++;
		}
		
		if (NUM3->value[count] == 1) {
			
			NUM3->value[0] += 1;
			NUM3->sign = 0;
		}
		if (NUM3->value[count] == 0) {
			NUM3->sign = 1; 
			
			for (i = 0; i < count; i++) {
				NUM3->value[i] = 9999 - NUM3->value[i];	
			}	
		}
		
	}
}

void storeInfor(HUGE_NUM *NUM, char *temp) {
	int i = 0;
	int j = 0;
	int k = 0;
	int flag = 1;


	if (temp[0] == '-') {
		NUM->sign = 1;
		for (i = 0; i < NUM->count-1; i++) {
			temp[i] = temp[i+1];
		}
		NUM->count--;

	} else {
		NUM->sign = 0;

	}

	for (j = NUM->count-1, i = 0; j >= NUM->count%4 + 3; i++) {
			NUM->value[i] = ((int)temp[j]-48) + ((int)temp[j-1] - 48) * 10 + ((int)temp[j-2] - 48)* 100 + ((int)temp[j-3] - 48)* 1000;
			j = j-4;
		}
		for (k = NUM->count%4-1,flag = 1; k >= 0; k--) {
			if (k == NUM->count%4-1) {
				NUM->value[i] = ((int)temp[k]-48) * flag;
			} else {
				NUM->value[i] += ((int)temp[k]-48) * flag;
			}
			flag = flag * 10;
		}

		printInfor(NUM);
		
}

int main () {
	
	FILE *fp1;
	FILE *fp2;

	char temp1[10000];
	char temp2[10000];

	int i = 0;
	int j = 0;
	int k = 0;
	int flag = 1;

	HUGE_NUM NUM1;
	HUGE_NUM NUM2;
	HUGE_NUM NUM3;


	fp1 = fopen("testdata1.txt","r");
	fp2 = fopen("testdata2.txt","r");

	fseek(fp1,0,SEEK_END);
	NUM1.count = ftell(fp1);
	fseek(fp2,0,SEEK_END);
	NUM2.count = ftell(fp2);
	if (NUM1.count >= NUM2.count) {
		NUM3.count = NUM1.count + 1;
	}  else {
		NUM3.count = NUM2.count + 1;
	}
	

	
	NUM1.value = (int *) calloc(sizeof(int), ((NUM1.count+3) / 4));
	NUM2.value = (int *) calloc(sizeof(int), ((NUM2.count+3) / 4));
	NUM3.value = (int *) calloc(sizeof(int), ((NUM3.count+3) / 4));	


	fseek(fp1,0,SEEK_SET);
	fseek(fp2,0,SEEK_SET);
	fgets(temp1,1000,fp1);
	fgets(temp2,1000,fp2);
	
	storeInfor(&NUM1,temp1);
	storeInfor(&NUM2,temp2);
	
	addNum(&NUM1, &NUM2, &NUM3, temp1, temp2);
	
	printInfor(&NUM3);
	



	fclose(fp1);
	fclose(fp2);
	return 0;
}

写的不太好,有什么问题大家可以指出来,欢迎在下面留言交流,乘法的程序后续我会更新出来的。

  • 0
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值