现在的位置: 首页 > 综合 > 正文

【无限长度】【绝对精度】的大数运算方法

2018年04月29日 ⁄ 综合 ⁄ 共 2943字 ⁄ 字号 评论关闭

    说到加减乘除,那应该是计算机的基本运算了,计算机之所以叫“计算机”就是因为拥有这些运算能力。但是当任何小的问题扩大化,都会成为一个大问题。因为计算机硬件的天然计算能力是限定位数的。例如32位CPU的运算能力就是2的32次方以内,64位CPU就是2的64次方以内。这在大多数的时候都是够用了,但在许多特殊情况下,却是不够用的。比如计算:107 的 50 次方;金融领域计算某个公式推演到20年后的情况,简化一点比如 1.102 的 20 次方,要求精确值,比如气象运算,医学模拟。在一些领域中2的64次是绝对不够用的!

    那么如何解决这个问题呢?答案是:【软件模拟CPU的硬件运算】

   这里我给出【大正整数】的【加法】和【乘法】,浮点数的加减乘除可以以此类推,后续也可能补充这部分的代码。

  1.这里是大正整数【加法】的运算函数,在注释部分有详细的工作机制说明

string add(string &a,string &b)
{	


	/**********************************/
	/*     采取模拟CPU运算器的方法    */
	/* 1.字符ASCII码可以进行加减乘除  */
	/*   例如:'0'+'1'= 48 + 49 =97   */
	/*         '6'+'1'-'0'='7'        */
	/*   所以:要进行两个字符的加法   */
	/*         可采用a+b-48           */
	/*         或者用a+b-'0'          */
	/*                                */
	/* 2.每次运算后考虑采用一个carry  */
	/*   记录进位,所以每次加法应该加 */
	/*   前次运算的进位,所以运算为:  */
	/*       a + b + carry - '0'      */
	/*                                */
	/* 3.要计算两个字符串代表的数值的 */
	/*	 加和,那么应该采用逆序运算:  */
	/*   比如:求"133"、"123"的和     */
	/*         运算次序应该是         */
	/*         3+3->3+2->1+1          */
	/*   这种求和顺序导致逆序记录更加 */
	/*   方便,即先记录成"652"         */
	/*   最后所有运算结束的时候再逆序 */
	/*   得到"256"                    */
	/*                                */
	/* 4.运算的时候应当考虑字符串长度 */
	/*   不等的情况,如"12""3456"     */
	/*   应先运算可运算的等长部分,即 */
	/*   "12""56"                     */
	/*   最后将"34"与进位加和的结果添 */
	/*   加到最终结果的前面           */
	/**********************************/




	int a_len = a.length();
	int b_len = b.length();
	
	/*存放最终结果*/
	string result;

	/*存放临时进位、临时结果*/
	int  carry = 0;
	char consq = 0;
	
	int i,j; /*i,j在循环退出后,仍然需要用到,故在循环外声明*/

	for(i=a_len-1,j=b_len-1; i>=0&&j>=0; --i,--j)   /*循环能直接相加的部分*/
	{	
		consq = a.at(i) + b.at(j) - 48 + carry;	
		carry = 0;
		if( consq > '9')
		{	
			carry = 1;
			consq = consq - 10;
		}
		result.push_back(consq);
	}

	if(i>=0)/*如果a仍然有没加完的部分*/
	{  
		while(i>=0)
		{
			consq = a.at(i) + carry;	
			carry = 0;
			if( consq > '9')
			{	
				carry = 1;
				consq = consq - 10;
			}
			result.push_back(consq);
			--i;
		}
	}
	else if( j>=0 )/*如果b仍然有没加完的部分*/
	{
		while(j>=0)
		{
			consq = b.at(j) + carry;	
			carry = 0;
			if( consq > '9')
			{	
				carry = 1;
				consq = consq - 10;
			}
			result.push_back(consq);
			--j;
		}
	
	}
	
	if(carry ==1)              /*处理最后的进位*/
	{	result.push_back('1');	}

	/*翻转*/
	std::reverse(result.begin(),result.end());

	return result;
};

2.这里是大正整数【乘法】的运算函数,注释部分有详细的工作机制说明

string multiply(const string& a, const string& b)
{

	/**********************************/
	/*     采取模拟CPU运算器的方法    */
	/* 1.字符ASCII码可以进行加减乘除  */
	/*   例如:('2'-'0')*('5'-'0')=10  */
	/*                                */
	/*   所以:要进行两个字符的乘法   */
	/*         可采用(a-'0')*(b-'0')  */
	/*                                */
	/* 2.每次运算后考虑采用一个carry  */
	/*   记录进位,所以每次加法应该加 */
	/*   前次运算的进位,所以运算为:  */
	/*   (a-'0')*(b-'0') + carry      */
	/*                                */
	/* 3.要计算两个字符串代表的数值的 */
	/*	 乘积,那么应该采用逆序记录, */
	/*   最后所有运算结束的时候再逆序 */
	/*                                */
	/* 4.乘积运算要考虑移位操作,但其 */
	/*   实可以用i、j循环变量确定当前 */
	/*   与上次循环结果的偏移量,其实 */
	/*   就是正序时就是i+j,但现在采用*/
	/*   逆序记录,那么就是           */
	/*   str_a.length -1 -i +         */
	/*   str_b.length -1 -j ;         */
	/**********************************/


	int a_len = a.length();
	int b_len = b.length();

	/*最终结果*/
	string result;

	/*设置初始结果,"0000...000",与b的位数相同*/
	result.assign(b_len,'0');



	/*临时结果、临时进位*/
	int consq = 0;
	int carry = 0;
	for(int i=a_len-1; i>=0; --i)
	{
		/* a中某一位数乘以b中每个数*/
		for(int j=b_len-1; j>=0; --j)
		{                                                  /*结果由三部分构成*/ 
			consq = (a.at(i)-'0') * ( b.at(j)-'0')         /*1. a*b的临时结果*/
				    + (result.at(a_len-i+b_len-j-2)-'0')   /*2. 最终结果中这个位置的值*/
					+ carry ;                              /*3. 前面运算的进位*/
			
			carry = 0;
			if( consq >9 )
			{
				int temp = consq/10;
				carry = temp;
				consq = consq - carry*10;
			}

			result.at(a_len-i+b_len-j-2) = (consq+'0');
		}
		                              /*处理最后的进位*/
		result.push_back(carry+'0');  /*可能是0-9,0是有意义的,可以保证for循环中consq运算时*/
		carry = 0;					  /*result.at(i+j)总是能够取到值的*/
		                              /*carry使用后清零*/
	}

	/*处理最终结果*/
	if( result.at(a_len+b_len-1) == '0')
	{	result.pop_back();	}

	/*翻转得到最终结果*/
	reverse(result.begin(),result.end());

	return result;
};

函数中用到了STL中的reverse算法,主要添加<algorithm>。其实可自己写,也比较简单。如果有Bug可以给我留言。

抱歉!评论已关闭.