算法之高精度(四)

这是高精度算法的第二篇,第一篇描述的是加法运算方式:
高精度加法

这一篇我们描述高精度减法。减法和加法有些类似。如果你理解了高精度数字加法完整的运算法则,我想理解减法也是非常容易的。我们仍然使用一个实例来帮助理解高精度减法的内容。不过,我们需要设置一个小小的限定:减法的最终结果一定是大于等于0。在高精度算法中,暂不考虑为负数的情况。
我们现需要计算684930287-501245612的值。
同样地,我们需要将两个数字转换成整数数组,并将其逆向存放。这是有必要的。

下标012345678
被减数782039486
减数216542105

这个表格和上一篇的表格几乎一模一样。我只是修改了最左侧一列的名称——从加数变为被减数与减数。
我们仍然回顾一下小学数学中所学习的减法法则。
从个位数开始,也就是表格中下标为0的位置,7减去2,得到结果55并没有<0,换句话说,7减5是减的开的,并不会出现负数。我们可以直接将将结果保留在该数位。如果结果小于0了,那么就需要考虑借位了。紧接着,下标为18减去1得到结果为7,这也是不需要借位的。下标为22减去6的结果为-4,是<0的,因此需要向高一位借位,借一位为10。因此,下标为2的数位上结果将变成-4+10。下一位的0就需要记为-1,这是比较合适的。-1减去5显然是不合适的。又需要向高位借位,使得3变成2,同样地,本位上-1-5+10的结果就可以成为正数4了。此时,下标已经来到4的位置。3被借走一位变成2,这是需要拿来减去4的,相减的结果显而易见,-8是没办法填入最终结果的,我们只能向高位再借一次位,使得-8+10变成正数2。下标为59是不怕借位的,被借完之后,其值为8,减去2绰绰有余。结果为6。下标为64减去1结果为3,不需要考虑借位。后续的8-06-5的结果均>0,只需要将计算结果放置在本数位上即可。
这是实例的完整解析。显得又长又麻烦。我们使用表格形式来表达会显得直观不少。

下标012345678
被减数782039486
减数216542105
借位000-1-1-1000
结果572-6+100+(-1)-5+103+(-1)-4+109+(-1)-2381

借位的位置发生在高位,我便将高位处设置为-1,以此表示借走了一位数字。在结果栏中,我采用加减法的方式表示,希望能够清晰地表达出数值是如何变化的。例如:下标为3的结果栏中显示0+(-1)-5+10的内容。0是被减数,-1是借给低位的数字,5是减数,10是向高位借来的数字。

我猜测,通过这样的描述,减法法则是表述完整的。下面我们尝试使用C++语言描述这个过程。

//假设a1,a2已经定义,并且存放的方式是逆序的——我的意思是个位数字放在下标为0的位置
//其中a1,a2的假设内容如下:
//a1 {7, 8, 2, 0, 3, 9, 4, 8, 6}
//a2 {2, 1, 6, 5, 4, 2, 1, 0, 5}

//高精度减法——a1为被减数,a2为减数
//这个减法函数假设两个减数的数位均不超过100位
void sub(int a1[], int a2[])
{
	int result[100] = {};	//存放两个高精度数字相减的结果
	for(int i = 0;i < 100; ++i)
	{
		int num = result[i];	//记录借位 有借位则为-1,无借位则为0
		num += a1[i] - a2[i];	//两个数相减的结果与借位的内容相加
		if(num < 0)		//小于0就需要考虑借位
		{
			num += 10;	//使其成为正数
			result[i + 1] = -1//借位会使高位减少1
		}
		result[i] = num;	//计算结果存放在本数位上
	}//	经过这层循环,所有的数位都已经计算结束了。
	
	//去前导0
	int k = 100 - 1;	//数组大小为100,下标最大为100-1
	while(result[k] == 0 && k > 0)	//k > 0 保证至少有一位数字输出
		--k;
	for(int i = k; i >= 0; --i)
		cout << result[i];	//从高位向低位输出
}

与高精度加法类似,这种描述方式将在sub函数内直接输出。对于某些情况而言,在函数内直接输出是不合时宜的。同样地,我们需要考虑到这种情况:684930287-501245612-13498
我们必须描述出能够将计算结果传递到函数外的方式。

void sub(int minuend[], int subtraction[])
{
	for(int i = 0;i < 100; ++i)
	{
		minuend[i] -= subtraction[i];	//被减数的某一位数字直接减去减数
		//减去之后结果可能小于0
		if(minuend[i] < 0)
		{
			minuend[i] += 10//使其变成正数
			//由于原位置存在数字,这里不能采用赋值运算,而是高位减去1
			minuend[i + 1] -= 1;	
		}
	}//经过此循环,减数和被减数的每一位都经过运算,得到结果存放在minuend数组中
}

//为了防止有些学习者不能完整描述本道例题的完整代码,在这里补充主函数中内容
int main()
{
	string minuend = "684930287";	//被减数
	string nums[2] = {"501245612", "13498"};		//两个减数
	int minuend_to_nums[100] = {};	//被减数转化为整数数组
	for(int i = 0; i < minuend.size(); ++i)
		minued_to_nums[i] = minued[minuend.size() - 1 - i] - '0';
	
	for(int i = 0;i < 2; ++i)	//两个减数
	{
		//使减数转化为整数数组
		int subtraction[100] = {};	//减数转化为整数数组
		string s = nums[i];
		for(int j = 0; j < s.size(); ++j)
			subtraction[i] = s[s.size() - 1 - i] - '0';
		
		//减法 —— 被减数 - 减数 
		//减法结果存放在被减数(minuend)数组中
		sub(minuend, subtraction);
	}
	
	//对结果去前导0
	int k = 100 - 1;
	while(minuend[k] == 0 && k > 0)
		--k;
	for(int i = k; i >= 0; --i)
		cout << minuend[i];
}

终于在经过一段比较长的代码后,我们完成了关于高精度减法的运算。我猜测,已经描述的足够完整。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值