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