ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

C++实现高精度计算器 (大数的加减乘除)

2021-12-30 13:30:34  阅读:250  来源: 互联网

标签:大数 int back C++ vector strB strA 加减乘除 size


对我们已知的能进行四则运算的数据类型,就算最长的长长整型也是拥有它的极限长度的,所以对于更大的数的四则运算,我们就要选择其他的类型进行接收,并定义适合它们的算法:

这个案例中建议选择,string类型数据去接收要计算的大数,再将其转成int型储存在动态容器vector中,可以方便的去实现我们这个案例

具体实现代码和算法都放在了下面,注释很详细。

实现效果:(测试懒得敲太多数,用小一点的数方便检测)直接上代码:

   具体思路看注释:

#include <iostream>
#include <vector>
#include <string>
#include <cmath>
#include<algorithm>
using namespace std;
class Compute
{
public:
    Compute() {};
    ~Compute() {};
    virtual void compute(string& strA, string& strB) = 0;
};
class Add : public Compute //这是加法类
{
public:
    virtual void compute(string& strA, string& strB) override;
};
void Add::compute(string& strA, string& strB)
{
    vector<int> a;   //第一个容器存放第一个数
    vector<int> b;   //第二个容器存放第二个数
    vector<int> sum; //和容器存放俩数之和

    int index = 0; //设置进位

    //首先将俩个数保存在容器中
    //并且是逆序保存  为了迎合容器的Pushback添加函数
    for (int i = strA.size() - 1; i >= 0; i--)
    {
        a.push_back(strA[i] - '0');
    }

    for (int i = strB.size() - 1; i >= 0; i--)
    {
        b.push_back(strB[i] - '0');
    }

    //然后开始加法算法,对两个数逐位相加
    //首先我们不管俩个数的长度大小,一直加到最短的那个数完全被加
    for (int i = 0; i < strA.size() && i < strB.size(); i++)
    {
        sum.push_back((a[i] + b[i] + index) % 10); //将逐位相加的结果储存在sum容器中
        index = (a[i] + b[i] + index) / 10;        //满10则进行进位
    }

    //然后开始考虑长度比较长的数的多余位置没有参加算法
    if (strA.size() > strB.size())
    {
        //这是A的长度比b大的情况,这时候已经计算到了b的长度位上
        for (int i = strB.size(); i < strA.size(); i++)
        {
            sum.push_back((index + a[i]) % 10);
            index = (a[i] + index) / 10;
        }
    }

    if (strB.size() > strA.size())
    {
        //这是B的长度比A大的情况,这时候已经计算到了a的长度位上
        for (int i = strA.size(); i < strB.size(); i++)
        {
            sum.push_back((index + b[i]) % 10);
            index = (index + b[i]) / 10;
        }
    }

    //计算完毕后判断进位  如果进位不为空那么还要往前进1
    if (index != 0)
    {
        sum.push_back(1);
        index = 0; //并且清空进位
    }

    //到这里已经完成加法算法
    //下面为输出代码

    //由于使用的容器较多,我选择使用迭代器输出sum  vector容器里的数据 即为最后的结果
    cout << strA << "+" << strB << "=";
    for (vector<int>::iterator it = sum.end() - 1; it >= sum.begin(); it--)
    {
        cout << *it;
        if (it == sum.begin())
        {
            break;
        }
    }
    cout << endl;

    //最后一步使用vector容器与自身交换  用来清空多余的无用内存
    vector<int>().swap(a);
    vector<int>().swap(b);
    vector<int>().swap(sum);
    //函数结束
}

class Sub : public Compute //这是减法类
{
public:
    virtual void compute(string& strA, string& strB) override;
    int Comparevector(vector<int>a, vector<int>b);//声明减法内置比较函数
};
void Sub::compute(string& strA, string& strB) //对基类计算函数进行重写
{
    vector<int> a;        //容器a存入第一个数据
    vector<int> b;        //容器b存入第二个数据
    vector<int> difvalue; //差值容器存入最后结果
    int lend = 0;         //定义借位数
                          //添加这一步是若两数相等则直接给结果,不需要经过算法;
    if (strA == strB)
    {
        difvalue.push_back(0);
        goto flag;
    }
    //因为减法运算还是从低位开始 那么我们还是 将数据倒序存入
    for (int i = strA.size() - 1; i >= 0; i--)
    {
        a.push_back(strA[i] - '0');
    }

    for (int i = strB.size() - 1; i >= 0; i--)
    {
        b.push_back(strB[i] - '0');
    }

    //下面开始减法算法
    //我选择使用绝对值算法 -> 首先放正负到一边,计算之前判断谁大谁小,保证永远使用大数减小数,最后再进行符号讨论;
    if (Comparevector(a, b) != 0)
    {
        //首先计算位数相同的地方
        for (int i = 0; i < strB.size(); i++)
        {
            if (a[i] < b[i])
            {
                for (int j = i + 1; j < strA.size(); j++)
                {
                    //向高位借位,如果高位不为0,则可以正常借到;
                    if (a[j] != 0)
                    {
                        lend = 1;
                        a[j] -= 1;
                        break;
                    } //如果高位为0,则向更高位借,0上有借位看为9;
                    else
                    {
                        a[j] = 9;
                    }
                }
                difvalue.push_back(lend * 10 + a[i] - b[i]);
                lend = 0;
            }
            else
            {
                difvalue.push_back(a[i] - b[i]);
            }
        }
        for (int i = strB.size(); i < strA.size(); i++)
        {
            difvalue.push_back(a[i]);
        }
    }
    else
    {
        //之后算法和之前相同
        //首先计算位数相同的地方
        for (int i = 0; i < strA.size(); i++)
        {
            if (b[i] < a[i])
            {
                for (int j = i + 1; j < strB.size(); j++)
                {
                    //向高位借位,如果高位不为0,则可以正常借到;
                    if (b[j] != 0)
                    {
                        lend = 1;
                        b[j] -= 1;
                        break;
                    } //如果高位为0,则向更高位借,0上有借位看为9;
                    else
                    {
                        b[j] = 9;
                    }
                }
                difvalue.push_back(lend * 10 + b[i] - a[i]);
                lend = 0;
            }
            else
            {
                difvalue.push_back(b[i] - a[i]);
            }
        }
        for (int i = strA.size(); i < strB.size(); i++)
        {
            difvalue.push_back(b[i]);
        }
    }
    //删 除 前 导 0
    for (int i = difvalue.size() - 1; i >= 0; i--)
    {
        if (difvalue[i] == 0)
        {
            difvalue.erase(difvalue.begin() + i);
        }
        else {
            break;
        }
    }
    //减法算法结束
    //下面为用迭代器输出
flag:
    cout << strA << "-" << strB << "=";
    //如果A比B小需要输出负号
    if (Comparevector(a, b) == 0)
    {
        cout << "-";
    }
    for (vector<int>::iterator it = difvalue.end() - 1; it >= difvalue.begin(); it--)
    {
        cout << *it;
        if (it == difvalue.begin())
        {
            break;
        }
    }
    cout << endl;
    //清内存
    vector<int>().swap(a);
    vector<int>().swap(b);
    vector<int>().swap(difvalue);
}
int Sub::Comparevector(vector<int>a, vector<int>b)//比较俩个vector容器内置的数的大小,传入的容器存的数是倒序的!
{
    //a比b打则返回1   a比b小则返回0   a与b等于则返回3
    //首先将数变成正序
    reverse(a.begin(), a.end());
    reverse(b.begin(), b.end());
    if (a.size() > b.size())
        return 1;
    if (a.size() < b.size())
        return 0;
    if (a.size() == b.size())
    {
        for (int i = 0; i < a.size(); i++)
        {
            if (a[i] > b[i])
                return 1;
            if (a[i] < b[i])
                return 0;
        }
        return 3;
    }
}

class Mul : public Compute //这是乘法类
{
    virtual void compute(string& strA, string& strB) override;
    vector<int> Muladd(vector<int>& a, vector<int>& b); //定义 乘法内置加法函数
};
void Mul::compute(string& strA, string& strB)
{
    vector<int> a;       // a容器中存放第一个数
    vector<int> b;       // b容器中存放第二个数
    vector<int> product; // product容器中存放俩数的乘积
    vector<int> temp;    // temp容器为工具容器 用于储存 计算过程中的步骤
    int index = 0;       //定义进位为0 Index
                         //乘法也是从小位开始算起 所以也采用逆序储存逆序输出
    for (int i = strA.size() - 1; i >= 0; i--)
    {
        a.push_back(strA[i] - '0');
    }

    for (int i = strB.size() - 1; i >= 0; i--)
    {
        b.push_back(strB[i] - '0');
    }

    for (int i = 0; i < b.size(); i++)
    {
        for (int j = 0; j < a.size(); j++)
        {
            temp.push_back((a[j] * b[i] + index) % 10);
            index = (a[j] * b[i] + index) / 10;
        }
        for (int j = 0; j < i; j++)
        {
            temp.insert(temp.begin(), 0);
        }
        //执行加法  加法执行完毕后清空temp容器
        product = Muladd(temp, product);
        temp.clear();
    }

    if (index != 0)
    {
        product.push_back(index);
    }
    //乘法计算结束
    //接下来为 迭代器输出储存乘积的容器 product
    cout << strA << "*" << strB << "=";
    for (vector<int>::iterator it = product.end() - 1; it >= product.begin(); it--)
    {
        cout << *it;
        if (it == product.begin())
        {
            break;
        }
    }
    cout << endl;
    //输出完毕 接下来进行容器清内存
    vector<int>().swap(a);
    vector<int>().swap(b);
    vector<int>().swap(temp);
    vector<int>().swap(product);
}
//对乘法内置加法函数的类外定义
vector<int> Mul::Muladd(vector<int>& a, vector<int>& b)
{
    //这个函数是实现两个容器的大数数据相加 与加法类一致
    int index = 0;   //设置进位
    vector<int> sum; //设置存放俩数和的容器 最后需要返回这个容器
    for (int i = 0; i < a.size() && i < b.size(); i++)
    {
        sum.push_back((index + a[i] + b[i]) % 10);
        index = (a[i] + b[i] + index) / 10;
    }
    //相等的位数已经相加完毕
    //接下来相加 独立的位数

    if (a.size() > b.size())
    {
        for (int i = b.size(); i < a.size(); i++)
        {
            sum.push_back((a[i] + index) % 10);
            index = (a[i] + index) / 10;
        }
    }

    if (a.size() < b.size())
    {
        for (int i = a.size(); i < b.size(); i++)
        {
            sum.push_back((b[i] + index) % 10);
            index = (b[i] + index) / 10;
        }
    }

    if (index != 0)
    {
        sum.push_back(1);
    }
    //计算完毕 返回算出的和容器vector
    //注意 这时候的 vector 仍然是小数在左。大数在右
    return sum;
}

class Division :public Compute//除法类
{
    virtual void compute(string& strA, string& strB) override;
    vector <int> divsub(vector<int>a, vector<int>b);//声明除法内置减法运算   运行的是第一个数减去第二个数
    int Compatrvector(vector<int>& a, vector<int>& b);//声明比较容器储存的数的大小的函数
};
void Division::compute(string& strA, string& strB)
{
    vector<int>a;//a容器存放第一个数 
    vector<int>b;//b容器存放第二个数
    vector<int>div;//div容器存放商

    //读取环节
    //因为除法的运算是从高位往低位进行运算 那么我们选择正向输入以方便运算
    for (int i = 0; i < strA.size(); i++)
    {
        a.push_back(strA[i] - '0');
    }

    for (int i = 0; i < strB.size(); i++)
    {
        b.push_back(strB[i] - '0');
    }

    //开始除法算法
    //1.将被除数位数用0补齐使得其长度和除数的长度相等
    //为了保留ab容器 利用复制构造创建新容器
    vector<int> B(b);
    vector<int> temp(a);
    //首先可以直接判断 俩数大小关系 如果前小于后直接输出0,没必要进行以下算法
    if (Compatrvector(a, b) == 0)
    {
        cout << strA << "/" << strB <<"=" << 0;
        return; 
    }

    if (b.size() == a.size())
    {
    int j = 0;
    while (Compatrvector(temp, B)!=0)
    {
        temp = divsub(temp, B);
        j++;
    }
    cout << strA << "/" << strB << "=" << j << endl;
    return;
  }

    int i;
    for (int i = B.size(); i < a.size(); i++)
    {
        B.push_back(0);
    }
    //2.判断补齐后的被除数与除数的大小关系(过程中不断更新除数,即每次差求商的余数)
    //->如果被除数的数值大于除数的数值 那么对被补齐的被除数进行退位操作,即退掉末位0   (似乎可以使用肯尼汉算法)
    //->如果被除数的数值小于除数的数值 那么进行除数循环减被除数求商 直到除数小于被除数
    //对以上操作进行循环
    //退出循环的条件 :补足的0已经退完 并且此时的除数小于被除数
    while (B.size() > b.size())
    {
        //这个只限于长度不相等  如果长度相等直接做减法很快得出答案!
        if (Compatrvector(B, temp) == 1)
        {

            B.pop_back();
            if (Compatrvector(B, temp) == 1)
            {
                div.push_back(0);
            }
        }
        if (Compatrvector(B, temp) != 1)
        {
            for (i = 1; i != 1.1; i++)
            {
                temp = divsub(temp, B);
                if (Compatrvector(temp, B) == 0)
                {
                    div.push_back(i);
                    i = 1;
                    break;
                }
            }
        }
    }
    //除法算法结束 
    //接下来为迭代器输出
    cout << strA << "/" << strB << "=";
    for (vector<int>::iterator it = div.begin(); it < div.end(); it++)
    {
        cout << *it;
    }
    //清理内存
    vector<int>().swap(a);
    vector<int>().swap(b);
    vector<int>().swap(temp);
    vector<int>().swap(div);
}
vector<int>Division::divsub(vector<int>a, vector<int>b)//为了防止参数被改变 采用 值传递
{
    vector<int> difvalue;
    //既然在除法里面的减法合理,那么一定是大数减小数  这里默认a>b
    //因为传进来的容器是正序的 所以为了计算方便 我们需要做倒序处理  
    reverse(a.begin(), a.end());
    reverse(b.begin(), b.end());
    int lend = 0;//初始化借位
    //既然已经默认了 a>b  那么a的位数一定大于等于b的位数
    for (int i = 0; i < b.size(); i++)
    {
        if (a[i] < b[i])
        {
            for (int j = i + 1; j < a.size(); j++)//a向高位非0数借位
            {
                if (a[j] != 0)
                {
                    a[j] -= 1;
                    lend = 1;
                    break;
                }
                else
                {
                    a[j] = 9;//向0借位为9
                }
            }
        }
        difvalue.push_back(lend * 10 + a[i] - b[i]);
        lend = 0;
    }
    //接下来为a多出的位数
    for (int i = b.size(); i < a.size(); i++)
    {
        difvalue.push_back(a[i]);
    }
    //然后要让差恢复正常顺序
    reverse(difvalue.begin(), difvalue.end());
    for (int i = 0; i != 1.1; i++)
    {
        //删除前导0
        if (difvalue[0] == 0&&difvalue.size()!=1)
        {
            difvalue.erase(difvalue.begin(), difvalue.begin() + 1);
        }
        else
        {
            break;
        }
    }
    return difvalue;
}
int Division::Compatrvector(vector<int>& a, vector<int>& b)//这是内置在除法类中的比较函数 用于比较俩个正向储存的容器数的大小
//若a>b  则返回1   小于则返回0      相等则返回3
{
    if (a.size() > b.size())
        return 1;
    if (a.size() < b.size())
        return 0;
    if (a.size() == b.size())
    {
        for (int i = 0; i < a.size(); i++)
        {
            if (a[i] > b[i])
                return 1;
            if (a[i] < b[i])
                return 0;
        }
        return 3;
    }
}

int main()
{
    //首先询问用户需要输入的两个大数
    string strA, strB;
    char idea;
    cout << "**************欢迎使用高精度计算器**************" << endl << endl;   
    cout<< "**************输入E X E退出计算器**************" << endl << endl;
    cout << "请输入你要计算的算式(符号数字之间由空格隔开)<示例:1空格+空格1>:" << endl;
flag:
    cin >> strA >> idea >> strB;
    if (strA == "E" && strB == "E" && idea == 'X')
    {
        goto tp;
    }
    //计算大类我使用了多态  下面对函数进行调用  基类指针指向派生类
    switch (idea)
    {
    case '+':
    {
        Compute* add = new Add;
        add->compute(strA, strB);
        if (add != NULL)
        {
            delete add;
            add = nullptr;
        }
        break;
    }
    case '-':
    {
        Compute* sub = new Sub;
        sub->compute(strA, strB);
        if (sub != NULL)
        {
            delete sub;
            sub = nullptr;
        }
        break;
    }
    case '*':
    {
        Compute* mul = new Mul;
        mul->compute(strA, strB);
        if (mul != NULL)
        {
            delete mul;
            mul = nullptr;
        }
        break;
    }
    case'/':
    {
        Compute* division = new Division;
        division->compute(strA, strB);
        if (division != NULL)
        {
            delete division;
            division = nullptr;
        }
        break;
    }

    }
    cout<<endl << "————————————————————————" << endl;
    goto flag;
tp:
    cout<< "*************--===感谢您的使用===--*************" << endl << endl;
    return 0;
}

标签:大数,int,back,C++,vector,strB,strA,加减乘除,size
来源: https://blog.csdn.net/qq_63403375/article/details/122234446

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有