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

来源:互联网 发布:数据挖掘岗很难进吗 编辑:程序博客网 时间:2024/05/22 00:39

    说到加减乘除,那应该是计算机的基本运算了,计算机之所以叫“计算机”就是因为拥有这些运算能力。但是当任何小的问题扩大化,都会成为一个大问题。因为计算机硬件的天然计算能力是限定位数的。例如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可以给我留言。


原创粉丝点击