文章目录

为什么需要大数加减类?
对于计算机而言,基本的数据类型一般最多为64位数据表示范围,这个范围是有限的,没法无限的表示所有的数据,那么有没有一种方式能够表示所有的大数,并完成加减乘除呢?
答案肯定是有的,由于数据都是由一位一位的数字所组成,我们只需要用数组中的每一位表示一位数字,便可完成对大数的模拟了。
那么我们说明时候需要用到大数模拟呢?对竞赛人而言,有很多题目实际上就是高精度大数模拟类型,而对于普通的程序员而言,大数模拟也仅是在做某个逻辑运算而保证不会溢出的最佳策略,那么大家难道不好奇如何实现一个大数模拟类吗?
现在就从封装一个简单的加减类开始了解这样一个大数模拟是怎么实现的👀
大数加减类实现详解
一、流程图总览
- 如图总体来说分为五部分:
- 静态成员函数:属于类的公共接口(核心)
- 构造和析构函数:构造对象以及析构对象
- 成员数据:用于运算的数据以及表示对象的数据
- 运算符重载:用于自定义运算方式(核心)
- 内部成员函数:属于对象的公共接口
二、成员数据和构造函数详解
- 成员数据
bool f; //是否是负数的标记
char *nums; //存储非符号的大数各个位
int length; //nums的数据长度
int capacity; //nums的可用容量
- 构造和析构
//缺省构造函数
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;<