新手实践小项目--大数加减类的实现(C++实现)✨

Posted C_YCBX Py_YYDS

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了新手实践小项目--大数加减类的实现(C++实现)✨相关的知识,希望对你有一定的参考价值。


为什么需要大数加减类?

对于计算机而言,基本的数据类型一般最多为64位数据表示范围,这个范围是有限的,没法无限的表示所有的数据,那么有没有一种方式能够表示所有的大数,并完成加减乘除呢?

答案肯定是有的,由于数据都是由一位一位的数字所组成,我们只需要用数组中的每一位表示一位数字,便可完成对大数的模拟了。

那么我们说明时候需要用到大数模拟呢?对竞赛人而言,有很多题目实际上就是高精度大数模拟类型,而对于普通的程序员而言,大数模拟也仅是在做某个逻辑运算而保证不会溢出的最佳策略,那么大家难道不好奇如何实现一个大数模拟类吗?

现在就从封装一个简单的加减类开始了解这样一个大数模拟是怎么实现的👀

大数加减类实现详解

一、流程图总览

  • 如图总体来说分为五部分:
  1. 静态成员函数:属于类的公共接口(核心)
  2. 构造和析构函数:构造对象以及析构对象
  3. 成员数据:用于运算的数据以及表示对象的数据
  4. 运算符重载:用于自定义运算方式(核心)
  5. 内部成员函数:属于对象的公共接口

二、成员数据和构造函数详解

  1. 成员数据
    bool f;			//是否是负数的标记
    char *nums;		//存储非符号的大数各个位
    int length;		//nums的数据长度
    int capacity;	//nums的可用容量
  1. 构造和析构
//缺省构造函数
BigInteger() : length(0), capacity(1), f(false) { 
        nums = new char[capacity];
    }
//用于转化普通字符串的构造函数
BigInteger(const char *n) : length(strlen(n)), f(false) { 
        int start = 0;
        if (n[0] == '-') {
            f = true;
            start++;
        }
        while (start<length&&n[start] == '0')start++;
        capacity = length * 10;
        nums = new char[capacity];
        std::copy(n + start, n + length, nums);
        length = length - start;
}
//拷贝构造函数
BigInteger(BigInteger &a) {  
        capacity = a.capacity;
        length = a.length;
        f = a.f;
        nums = new char[capacity];
        std::copy(a.nums, a.nums + length, nums);
}
//移动构造函数:这里调用了等于号,根据a的类型来决定用哪个等于号
BigInteger(BigInteger &&a) :length(0){
        *this = a;
}
//析构函数
~BigInteger() { 
        delete[] nums;
}

三、(算法核心)静态成员函数和运算符重载详解

static Swap()

//调用std的swap实现对基本数据的交换
static void Swap(BigInteger &a, BigInteger &b) {
        std::swap(a.length, b.length);
        std::swap(a.capacity, b.capacity);
        std::swap(a.f, b.f);
        std::swap(a.nums, b.nums);
}

static compare()

//不看符号比较nums的大小:表示a是否比b大
static bool compare(const BigInteger &a,const BigInteger &b) { //比较纯nums大小(不看符号
        int n1 = a.length;
        int n2 = b.length;
        if (n1 != n2)return n1 > n2; //返回a和b哪个大,true则位a大
        int i = 0;
        while (i < n1 && a.nums[i] == b.nums[i])i++; //a b一样长的情况下,比较两个的值
        if (i == n1)
            return false;
        return a.nums[i] > b.nums[i];
}

static isEqual()

//表示a和b是否相等
bool isEqual(BigInteger &a, BigInteger &b) {
        if (a.f != b.f || (a.length != b.length))return false;
        int i = 0;
        while (i < a.length && a.nums[i] == b.nums[i])i++;
        return i == a.length && a.f == b.f;
}

(*核心算法)static add()

不看符号的加法,符号这方面由重载加法运算符控制。

static BigInteger add(BigInteger &a, BigInteger &b) { //不看符号的加法
    a.reverse();//尾端对齐
    b.reverse();
    BigInteger t;
    int up = 0;
    int len = a.length > b.length ? a.length : b.length;
    for (int i = 0; i < len; i++) {
        int ta = i < a.length ? a[i] - '0' : 0;
        int tb = i < b.length ? b[i] - '0' : 0;
        int base = ta + tb + up;
        t.push_back(base % 10 + '0');
        up = base / 10;
    }
    if (up)
        t.push_back(up + '0');
    t.reverse();//返回原位
    a.reverse();
    b.reverse();
    return t;
}

(*核心算法)static minus()

不看符号的减法,默认了a的nums大小(不看符号)是比b大的。

static BigInteger minus(BigInteger &a, BigInteger &b) {
    a.reverse();
    b.reverse();
    BigInteger t;
    int len = a.length > b.length ? a.length : b.length;
    for (int i = 0; i < len; i++) {
        int ta = i < a.length ? a[i] - '0' : 0;
        int tb = i < b.length ? b[i] - '0' : 0;
        int base = ta - tb;
        if (base < 0) {
            base += 10;
            a[i + 1]--;
        }
        t.push_back(base + '0');
    }
    t.reverse();
    a.reverse();
    b.reverse();
    return t;
}

char &operator[]

char &operator[](int i) {
        return nums[i];
}

BigInteger &operator=

用了两个版本–右值引用和左值引用版本

  • 右值引用延长寿命,用交换的方式实现(毕竟是将亡值
BigInteger &operator=(BigInteger&& a) { //Swap&Copy方式实现右值赋值重载
    Swap(*this, a);
    return *this;
}
  • 左值引用深拷贝
BigInteger &operator=(const BigInteger &a) {//深拷贝
        if (length != 0)//如果不是初始化调用的 = ,则肯定需要先把原来的内存delete掉
            delete[]nums;
        capacity = a.capacity;
        length = a.length;
        f = a.f;
        nums = new char[capacity];
        std::copy(a.nums, a.nums + length, nums);
        return *this;
}

bool operator<

重载了小于号,好处在于可以直接利用stl进行各种排序操作了。

注意:一定要写成const版本的成员函数,不然STL库无法调用,因为STL库中的所有比较都是基于const对象。

bool operator<(const BigInteger &a) const {
        if (f && !a.f) { //其中一个为负数,则那个是更小的
            return true;
        } else if (!f && a.f)
            return false;
        if (f) { //两者都为负数的情况,左边的值要更大则为true
            return compare(*this, a);
        }//两者均为正数,则值更小的在左边为true
        return compare(a, *this);
}

(*核心算法)BigInteger operator+

利用静态成员函数完成无符号的加减,然后在这里进行判断各种符号情况,根据不同的符号情况进行不同的加法处理。

  • 注意在调用minus之前需要比较两个数的nums谁更大,更大的放在第一个参数上!
BigInteger operator+(BigInteger &a) {
        BigInteger res;
        bool flag;
        if (a.f && f) { //同为负数情况,直接相加,再改符号
            res = add(*this, a);
            flag = true;
        } else if (a.f && !f) {//左正右负
            if (compare(a, *this)) { //看负数对应的nums是否比正数大
                flag = true;
                res = minus(a, *this);
            } else {
                flag = false;
                res = minus(*this, a);
            }
        } else if (!a.f && f) {
            if (compare(*this, a)) { //与上一个相同
                flag = true;
                res = minus(*this, a);
            } else {
                flag = false;
                res = minus(a, *this);
            }
        } else { //同时为正数就是最简单的加法
            flag = false;
            res = add(*this, a);
        }
        res.f = flag;
        return res;
}

(*核心算法)BigInteger operator-

同样是分类讨论,同样是根据不同的类型调用minus和add函数。

  • 与正数不同的处理在于对符号的处理,如果同为负数,则需要判断两者是否相等,防止两数相等相减后减为 0,而被处理为 -0
BigInteger operator-(BigInteger &a) {
        BigInteger res;
        bool flag;
        if (a.f && f) { //同为负数情况--左边-右边==(不看符号)右边-(不看符号)左边
            if (compare(a, *this)) {
                flag = false;
                res = minus(a, *this);
            } else {
                if (isEqual(*this, a))
                    flag = false;
                else flag = true;
                res = minus(*this, a);
            }
        } else if (a.f && !f) { //左边为正,右边为负--左边-右边==左边+右边
            flag = false;
            res = add(a, *this);
        } else if (!a.f && f) { //右边为正,左边为负--左边-右边==两边为负的加法
            flag = true;
            res = add(a, *this);
        } else { //同时为正数--左边-右边==左边-右边(分类讨论正负
            if (compare(a, *this)) { //右边>左边,符号为负
                res = minus(a, *this);
                flag = true;
            } else { //右边<左边,符号为正
                res = minus(*this, a);
                flag = false;
            }
        }
        res.f = flag;
        return res;
}

四、其他内部成员函数详解

  1. 向外提供的get接口
  int getCap() {
        return capacity;
    }

    int getLength() {
        return length;
    }

    bool isNegative() {
        return f;
    }

    bool isEmpty() {
        return length == 0;
    }
  1. 进行赋值操作所必备的push_backreverse函数
    void push_back(char x) {
        if (length >= capacity) {//扩容操作
            capacity *= 2;
            char *t = nums;
            nums = new char[capacity];
            std::copy(t, t + length, nums);
            delete[]t;
        }
        nums[length++] = x;
    }

    void reverse() {//反转操作
        int l = 0, r = length - 1;
        while (l < r) {
            std::swap(nums[l], nums[r]);
            l++;
            r--;
        }
    }
  1. 无关紧要的 read() 输入接口 和 print() 输出测试接口
    void print() {
        if (f)
            printf("-");
        nums[length] = '\\0';
        int i = 0;
        while (nums[i] == '0')i++;
        printf("%s", nums + i);
    }

    void read() {//利用getchar()给对象赋上数据
        char c = getchar();
        if (c == '-') {
            f = true;
            c = getchar();
        }
        while (c == '0') c = getchar();//将前导0消耗掉
        while (c != '\\n') {
            push_back(c);//不断的调用push_back即可
            c = getchar();
        }
    }

整理代码

.h声明文件

如果在声明类的同时进行定义,则内部的成员函数默认就是内联的。所以我们一般把短小的代码进行内联,以下的实现均是以该规律进行。

//
// Created by Alone on 2021/10/7.
//

#ifndef MY_TINY_STL_BIGINTEGER_H
#define MY_TINY_STL_BIGINTEGER_H

#include <algorithm>
#include <iostream>
#include <cstring>

class BigInteger {
    bool f;
    char *nums;
    int length;
    int capacity;
public:
    //构造函数
    BigInteger() : length(0), capacity(1), f(false) { //缺省构造函数
        nums = new char[capacity];
    }

    BigInteger(const char *n);

    BigInteger(const BigInteger &a);

    BigInteger(BigInteger &&a);

    ~BigInteger

以上是关于新手实践小项目--大数加减类的实现(C++实现)✨的主要内容,如果未能解决你的问题,请参考以下文章

C++中的大数乘的实现

大数(高精度)加减乘除

大数加减法 - java实现

大数运算(加减乘除)

大数运算实现加减乘除四则运算

C++ 大数运算(加减乘除取模)