【C语言>数据结构与算法的应用4】巨大数----加减乘运算(万进制和Mec补码的应用)

巨大数

  • 什么是巨大数
      • 目的
  • 巨大数的加法
      • 巨大数的存储
      • 万进制
      • 微易码补码
  • 巨大数的减法
  • 巨大数的乘法
  • 总结

什么是巨大数

巨大数其实就是有效数字位很大,可表示数的大小超过了int 的表示范围:[-217483648,2147483647],虽然float、double类型可表示的数的范围很大,分别为:3.4E-38~3.4E+38、1.7E-308~1.7E+308。但是它们的有效数字位却不大,分别为:6 – 7位、5 – 16位。这时我们需要一个可以表示很大有效数字位的数—巨大数

目的

为了解决超出计算机可表示范围数据的存储以及运算,如果按照平常的计算方法便会无能为力,这时,便需要一种可以解决更大位数计算的方法,也就是巨大数四则运算所存在的意义。而且我们的巨大数还可以为小数,因此起到增加计算精度的作用,本篇文章将重点讲述巨大数的存储以及四则运算。

巨大数的加法

巨大数的存储

上面说到,巨大数会超过int、float、double的表示范围,所以我们不能用它们来存储巨大数,这里我们用字符串,也就是定义一个char类型的数组,来存储巨大数,然后将它转化为整型量,存储到int类型的数组中(采用高高低低原则数组的高位存储的是数据的高位,数组的低位存储的是数据的低位),即数据的个十百千位的四个数,存储到数组的第一个元素上,再参与运算。

巨大数结构体

typedef struct HUGE_NUMBER
{
     
	boolean sign; //存储巨大数符号
	int *data; //存储巨大数的数字
	int count; //数组元素的个数
	int length; //巨大数位数
	int pow; //权重,多少位小数
}HUGE_NUMBER;

万进制

这里我们引入万进制,主要是为了效率问题,万进制,顾名思义,以万进1,其实万进制存储也就是用int类型的数组,四位一存,然后四位四位的进行运算,和我们所理解的十进制其实大同小异。如果,万进制运算效率高,那么我们为什仫不采用十万进制呢? 这样运算效率会更高,因为万进制一个位存储的最大9999(大于9999要进位),在做乘法计算时即使9999乘以9999,依然可以暂时放在这个位上,因为每一个位都是int类型的,整体是一个int类型的数组,最后再做统一处理。

微易码补码

这是巨大数的核心技术,相当厉害。

  1. 微易码补码的原理,类似计算机中原码和补码的转化:正数,原码 = 补码;负数,补码 = 原码按位取反,末位加1。微易码补码的原理:正数,等于其本身;负数,9999 - 这个数的绝对值。
  2. 为什么要引入《微易码补码》?在进行加减法运算时,减法可以转化为加法运算,那么减法其实就是加法,再进行加法过程中,运算数是带符号位的,有可能是负数。所以我们引入微易码补码就是为了减少因为负数而引起的复杂运算,这样简化运算。
    【C语言>数据结构与算法的应用4】巨大数----加减乘运算(万进制和Mec补码的应用)_第1张图片
  3. 上面简单的举了个例子,来说明微易码补码,下面将详细介绍微易码补码的神奇之处:
    【C语言>数据结构与算法的应用4】巨大数----加减乘运算(万进制和Mec补码的应用)_第2张图片
    【C语言>数据结构与算法的应用4】巨大数----加减乘运算(万进制和Mec补码的应用)_第3张图片
    【C语言>数据结构与算法的应用4】巨大数----加减乘运算(万进制和Mec补码的应用)_第4张图片
    【C语言>数据结构与算法的应用4】巨大数----加减乘运算(万进制和Mec补码的应用)_第5张图片
  4. 加法运算的代码:
void addHUGE(HUGE_NUMBER *hn1, HUGE_NUMBER *hn2, HUGE_NUMBER *result)
{
     
	int carry = 0;
	int sum;
	int i;

	result->count = hn1->count > hn2->count ? (hn1->count + 1): (hn2->count + 1);  //运算结果存在着进位的可能,所以要多申请一个空间;
	//若不进位,正数,这个空间将会补 0000 ;负数,这个空间将会补 9999。
	//建议可以变量跟踪一下。
	result->data = (int *)calloc(sizeof(int), result->count);
	for (i = 0; i < result->count; i++) {
     
		if (i >= hn1->count) {
     
			hn1->data[i] = 0;
		}
		if (i >= hn2->count) {
     
			hn2->data[i] = 0;
		}
		sum = getMecCode(hn1->data[i], hn1->sign) + getMecCode(hn2->data[i], hn2->sign) + carry;
		carry = sum / 10000;
		sum = sum % 10000;
		result->data[i] = sum;
	}
	result->data[0] += carry; //有进位情况下,运算的结果和正确结果相差1, 这里加上进位。
	if (hn1->sign ^ hn2->sign ^ carry) {
      //得到运算结果的符号,若为负号,要将补码转化。
		result->sign = NEGATIVE;
		for (i = 0; i < result->count; i++) {
     
			result->data[i] = getMecCode(result->data[i], result->sign);
		}
	}
	else {
     
		result->sign = POSITIVE;
	}	
}
int getMecCode(int data, char sign) 
{
     
	return ((sign == 1) ? (9999 - data) : data);
}

巨大数的减法

巨大数的减法和加法一样。减法就是将一个运算数取相反数,再调用加法函数,就ok了。

void subHUGE(HUGE_NUMBER *hn1, HUGE_NUMBER *hn2, HUGE_NUMBER *result)
{
     
	hn2->sign = (hn2->sign == 1) ? 0 : 1;
	addHUGE(hn1, hn2, result);
	hn2->sign = (hn2->sign == 1) ? 0 : 1;
}

巨大数的乘法

巨大数的乘法比较简单,它不需要用到微易码补码,只需要用万进制存储,就OK了。
【C语言>数据结构与算法的应用4】巨大数----加减乘运算(万进制和Mec补码的应用)_第6张图片
四位一存,每次相错4位。

void mulHUGE(HUGE_NUMBER *hn1, HUGE_NUMBER *hn2, HUGE_NUMBER *result)
{
     
	int carry = 0;
	int sum;
	int i;
	int j;
	int t = 0;

	result->count = (hn1->count > hn2->count ? hn1->count : hn2->count) * 2;
	result->data = (int *)calloc(sizeof(int), result->count);
	result->sign = hn1->sign ^ hn2->sign;
	for (i = 0; i < hn1->count; i++) {
     
		t = i;
		for (j = 0; j <= hn2->count; j++) {
     
			if (j == hn2->count) {
     
				hn2->data[j] = 0;
			}
			sum = hn1->data[i] * hn2->data[j] + carry;
			carry = sum / 10000;
			sum = sum % 10000;
			result->data[t] += sum; //错位相加
			carry += result->data[t] / 10000; //保证每个数组元素中只存储四位
			result->data[t] = result->data[t] % 10000;
			t++;
		}
	}
}

运行结果:
【C语言>数据结构与算法的应用4】巨大数----加减乘运算(万进制和Mec补码的应用)_第7张图片

总结

首先,感谢铁血教主的指导,在这个巨大数项目中,对于万进制和微易码补码的运用,我感到很凶悍,这些方法确实让人感到新颖,教主nb~。同时,在我完成过程中,觉得一定要注意“手工过程”和“变量跟踪”,许多错误在编写时,是很难发现的。所以,在编写前,先进行“手工过程”,而且一定要充足,这样整理和开阔你的思路,有时候你敲几个小时的代码,都不如半个小时的手工过程。同时和同学一起进行讨论也是一种开阔思维的方法。

你可能感兴趣的:(c语言,算法,数据结构)