高精度整数类
采用顺序存储结构, 用动态数组保存整数的每一数位, 根据整数的大小自动调整数组的尺寸, 实现动态内存管理。仿照C++的整数类型, 实现整数的全部运算。
话不多说,上代码:
/*BigInteger.h*/
#pragma once
#include<iostream>
#include<string>
#include<vector>
#define POSITIZE 1 //非负位
#define NEGATIVE -1 //负位
using namespace std;
class BigInteger
{
public:
//构造函数
BigInteger();//默认构造函数
BigInteger(long long int);//int参数构造函数
BigInteger(const BigInteger& a);//拷贝构造函数
void zero_justify();//消除前导0
friend ostream& operator<<(ostream& cout, BigInteger b);//重载输出
friend istream& operator>>(istream& cin, BigInteger& b);//重载输入
BigInteger operator << (int);//重载左移
//比较
friend int compare(const BigInteger& a, const BigInteger& b);//比较函数
friend bool operator < (const BigInteger& a, const BigInteger& b);
friend bool operator <= (const BigInteger& a, const BigInteger& b);
friend bool operator > (const BigInteger& a, const BigInteger& b);
friend bool operator >= (const BigInteger& a, const BigInteger& b);
friend bool operator == (const BigInteger& a, const BigInteger& b);
friend bool operator != (const BigInteger& a, const BigInteger& b);
friend bool iszero(const BigInteger& a);//判断是否为0
//重载 + - * / = ^运算符
friend BigInteger operator + (BigInteger a, BigInteger b);
BigInteger operator += (const BigInteger& b);
friend BigInteger operator - (BigInteger a, BigInteger b);
BigInteger operator -= (const BigInteger& b);
friend BigInteger operator * (BigInteger a, BigInteger b);
BigInteger operator *= (const BigInteger& b);
friend BigInteger operator / (BigInteger a, BigInteger b);
BigInteger operator /= (const BigInteger& b);
friend BigInteger operator % (BigInteger a, BigInteger b);
BigInteger operator %= (const BigInteger& b);
BigInteger operator = (const BigInteger& b);
BigInteger operator ^ (int n);
//后置 递增 递减
BigInteger operator++(int);
BigInteger operator--(int);
//前置 递增 递减
BigInteger& operator++();
BigInteger& operator--();
private:
vector<int> value;//数位值 倒置存储 首位为个位
int sign;//符号位 -1代表非负数 1代表非负数 采用int类型有利于后续符号位的计算 判断符号时将两个数的符号位相乘即为结果 但当2个数有一方为0时要注意判断
};
下面对几个关键函数做说明(最后有全部代码):
一、BigInteger(long long int);//int参数构造函数
//整数参数构造
BigInteger::BigInteger(long long int a)
{
sign = (a >= 0) ? POSITIZE : NEGATIVE;//判断符号
long long int x;
x = abs(a);
do
{
this->value.push_back(x % 10);
x /= 10;
} while (x);
}
这个构造函数必须要有,如果没有就无法兼容 BigInteger类型和int类型的运算。比如 :
friend BigInteger operator + (BigInteger a, BigInteger b);
BigInteger a, b;
a = b + 1000000;
因为 有构造函数:BigInteger(long long int); 上式中的1000000会自动类型转换为BigInteger类型。
二、比较函数friend int compare(const BigInteger& a, const BigInteger& b);//比较函数
其实这个函数的最大作用就是避免各种比较运算符的代码重复问题,就写一个函数,其他比较函数调用它就行,返回值为-1表示小于,0表示等于,1表示大于。
//比较函数 也是其他判断函数的内核 返回值 为 -1 0 1 分别代表 小于 等于 大于
int compare(const BigInteger& a, const BigInteger& b)
{
if (a.sign < b.sign) //左值为负数 右值为正数时 直接返回小于(-1)
{
return -1;
}
else if(a.sign > b.sign)//左值为正数 右值为负数时 直接返回大于(1)
{
return 1;
}
//符号相同时:
if (a.value.size() > b.value.size())//左值数位大于右值 由于符号相同 可以直接判断左值大于右值
{
return a.sign;
}
else if (a.value.size() < b.value.size())//与上同理
{
return a.sign * (-1);
}
for (int i = a.value.size() - 1; i >= 0; i--)//符号相同 数位相同 则从最高位开始比较
{
//此时比较有两方面要考虑 1:同位的数值大小比较 2:2个数的符号是同正 还是同负
if (a.value[i] > b.value[i])//左值大于右值
{
return a.sign;//如果同为正 左值大于右值 反而左值小 所以直接返回左值的符号即可
}
if (a.value[i] < b.value[i])//左值小于右值
{
return a.sign * (-1);//与上同理
}
}
return 0;
}
三、重载乘法运算符 friend BigInteger operator * (BigInteger a, BigInteger b);
其实原理很简单,我们小学学的乘法时怎么算的我们就怎么算。比如:
123456 * 123
注意:此函数中 用了重载的+运算符
//重载*
BigInteger operator * (BigInteger a, BigInteger b)
{
BigInteger ret(0);//返回值 默认为0
if (iszero(a) || iszero(b))//a b 任一为0 直接返回0
{
return ret;
}
else
{
//用ret保存结果 curr储存中间值 两数 从最低为开始相乘 将a依次与b的每一位相乘(从个位开始)得到curr 将curr累加得到ret即为结果
int m = a.value.size(), n = b.value.size();
for (int i = 0; i < n; i++)//i代表b的当前位数 从个位开始
{
BigInteger curr;
for (int j = i; j > 0; j--)
{//i每前进一位 curr都要左移一位 即乘10 如 b=123 则a要依次乘 3 20 100
curr.value.push_back(0);
}
int add = 0;//进位数
for (int j = 0; j < m; j++)//此处 j控制 a的位数
{
int p = a.value.at(j) * b.value.at(i) + add;//与加法相似 将个位存入 >=10的部分累加到进位数中
curr.value.push_back(p % 10);
add = p / 10;
}
while (add != 0)//进位数不为零 则直接将其顺位加入到高位中
{
curr.value.push_back(add % 10);
add /= 10;
}
ret += curr;//累加到结果当中
}
}
ret.zero_justify();
ret.sign = a.sign * b.sign;//最后判断负号
return ret;
}
完整代码:
三个构造函数:
//默认构造
BigInteger::BigInteger()
{
sign = POSITIZE;//默认为非负
}
//整数参数构造
BigInteger::BigInteger(long long int a)
{
sign = (a >= 0) ? POSITIZE : NEGATIVE;//判断符号
long long int x;
x = abs(a);
do
{
this->value.push_back(x % 10);
x /= 10;
} while (x);
}
//拷贝构造
BigInteger::BigInteger(const BigInteger& a)
{
this->value = a.value;
this->sign = a.sign;
}
消除前导0:
//清除前导0
void BigInteger::zero_justify()
{
for (int i = this->value.size() - 1; i >= 1;i--)//从最高位开始
{
if (this->value[i])//不为零就退出
{
break;
}
else
{
this->value.pop_back();//为零则清除此零
}
}
if (this->value.size() == 1 && this->value[0] == 0)//值为0则不清除
{
this->sign = POSITIZE;
}
}
重载输出、输入:
//重载输出
ostream& operator<<(ostream& out, const BigInteger b)
{
if (b.sign < 0)//先判断符号 如果是负数 则先输出负号
{
out << '-';
}
for (int i = b.value.size() - 1; i >= 0; i--)//从最高位开始输出
{
out << b.value[i];
}
return out;//返回值为out 满足连续输出的需求
}
//重载输入
istream& operator>>(istream& in, BigInteger& b)
{
string s;//以string方式输入
in >> s;
if (s.empty())//如果为空则直接退出
{
return in;//满足连续输入
}
if (isdigit(s[0]))//首位无符号 默认为正数
{
b.sign = POSITIZE;
}
else//有符号
{
b.sign = (s[0] == '+') ? POSITIZE : NEGATIVE;//判断符号位是否为'-'
s.erase(s.begin());//符号判断完成后清除符号位 方便后续运算
}
b.value.clear();//输入前先清空
for (int i = s.size() - 1; i >= 0; i--)
{
b.value.push_back(s[i] - '0');//从最高位开始入栈 并将每一位由char类型转换为int压入栈内
}
b.zero_justify();//清除前导0
return in;//满足连续输入
}
重载左移运算符 即*10操作:
//重载左移运算符 即*10操作
BigInteger BigInteger::operator << (int n)
{
if (this->value.size() == 1 && this->value[0] == 0)//如果数值为0 则直接返回本身 即返回0
{
return *this;
}
this->value.insert(this->value.begin(), n, 0);//直接插入n个0
return *this;
}
比较函数 也是其他判断函数的内核 返回值 为 -1 0 1 分别代表 小于 等于 大于:
//比较函数 也是其他判断函数的内核 返回值 为 -1 0 1 分别代表 小于 等于 大于
int compare(const BigInteger& a, const BigInteger& b)
{
if (a.sign < b.sign) //左值为负数 右值为正数时 直接返回小于(-1)
{
return -1;
}
else if(a.sign > b.sign)//左值为正数 右值为负数时 直接返回大于(1)
{
return 1;
}
//符号相同时:
if (a.value.size() > b.value.size())//左值数位大于右值 由于符号相同 可以直接判断左值大于右值
{
return a.sign;
}
else if (a.value.size() < b.value.size())//与上同理
{
return a.sign * (-1);
}
for (int i = a.value.size() - 1; i >= 0; i--)//符号相同 数位相同 则从最高位开始比较
{
//此时比较有两方面要考虑 1:同位的数值大小比较 2:2个数的符号是同正 还是同负
if (a.value[i] > b.value[i])//左值大于右值
{
return a.sign;//如果同为正 左值大于右值 反而左值小 所以直接返回左值的符号即可
}
if (a.value[i] < b.value[i])//左值小于右值
{
return a.sign * (-1);//与上同理
}
}
return 0;
}
比较运算符重载:
//以下重载的比较函数 直接调用compare函数即可满足需求
//小于
bool operator < (const BigInteger& a, const BigInteger& b)
{
return compare(a, b) < 0;
}
//小于等于
bool operator <= (const BigInteger& a, const BigInteger& b)
{
return compare(a, b) <= 0;
}
//大于
bool operator > (const BigInteger& a, const BigInteger& b)
{
return compare(a, b) > 0;
}
//大于等于
bool operator >= (const BigInteger& a, const BigInteger& b)
{
return compare(a, b) >= 0;
}
//相等
bool operator == (const BigInteger& a, const BigInteger& b)
{
return compare(a, b) == 0;
}
//不相等
bool operator != (const BigInteger& a, const BigInteger& b)
{
return compare(a, b) != 0;
}
判断数值为0:
bool iszero(const BigInteger& a)
{
bool ret = false;
if (a.value.size() == 1 && a.value[0] == 0)
{
ret = true;
}
return ret;
}
重载加减乘除取余运算符+ - * / % ^:
注意:除法运算 被除数为零时 直接是报错
//重载+
BigInteger operator + (BigInteger a, BigInteger b)
{
BigInteger c;//存放结果
if (a.sign == b.sign)//两数符号相等 则结果的符号与两数符号相同
{
c.sign = a.sign;
}
else//两数符号不等 则转为减法运算再输出结果
{
if (a.sign == POSITIZE)//a为正 b为负 直接调用 a-b
{
b.sign = POSITIZE;
c = a - b;
}
else
{
a.sign = POSITIZE;//a为负 b为正 直接调用 b-a
c = b - a;
}
return c;
}
int i = 0, j = 0, add = 0;
while (i < a.value.size() || j < b.value.size() || add != 0)//从个位开始计算
{
//对应位的数值相加 存入c当中 当2数中的某个数算完时 则另一个数对应位的数值加0存入c中
//三个步骤:1:对应位数值相乘 得到 ret ret可能>=10 2:ret大于10的部分为进位数 存入add当中 3:ret%10即为当前结果位的相应数值存入结果即可
int x = i < a.value.size() ? a.value.at(i) : 0;
int y = j < b.value.size() ? b.value.at(j) : 0;
int ret = x + y + add;//用add储存进位数
c.value.push_back(ret % 10);//将当前位数数值存入
add = ret / 10;//保存进位数
i++; j++;
}
c.zero_justify();//消除前置0
return c;
}
//重载+=
BigInteger BigInteger::operator += (const BigInteger& b)
{
*this = *this + b;
return *this;
}
//重载-
BigInteger operator - (BigInteger a, BigInteger b)
{
BigInteger c;//保存结果
if (a.sign < 0 || b.sign < 0)//当两数符号不同 转换为+法即可
{
b.sign *= -1;
c = a + b;
return c;
}
if (a < b)// a b 符号相同 a小于b 结果一定为负数 转换为 b-a 再将结果的符号改为负号即可
{
c = b - a;
c.sign = NEGATIVE;
return c;
}
//a b负号相同 a >= b
c.sign = POSITIZE;//结果一定非负
int borrow = 0, i = 0, j = 0, ret; // 借位
while (i < a.value.size() || j < b.value.size())//从最高位开始计算
{
int x = i < a.value.size() ? a.value.at(i) : 0;// 任一数位数不够 转为0即可 不做过多解释 与加法同理
int y = j < b.value.size() ? b.value.at(j) : 0;
int ret = x - borrow - y;//borrow为借位
if (ret < 0)//当x < y 即出现不够减的情况需要向高位借位
{
ret += 10;
borrow = 1;
}
else
{
borrow = 0;
}
c.value.push_back(ret % 10);
i++; j++;
}
c.zero_justify();
return c;
}
//重载-=
BigInteger BigInteger::operator -= (const BigInteger& b)
{
*this = *this - b;
return *this;
}
//重载*
BigInteger operator * (BigInteger a, BigInteger b)
{
BigInteger ret(0);//返回值 默认为0
if (iszero(a) || iszero(b))//a b 任一为0 直接返回0
{
return ret;
}
else
{
//用ret保存结果 curr储存中间值 两数 从最低为开始相乘 将a依次与b的每一位相乘(从个位开始)得到curr 将curr累加得到ret即为结果
int m = a.value.size(), n = b.value.size();
for (int i = 0; i < n; i++)//i代表b的当前位数 从个位开始
{
BigInteger curr;
for (int j = i; j > 0; j--)
{//i每前进一位 curr都要左移一位 即乘10 如 b=123 则a要依次乘 3 20 100
curr.value.push_back(0);
}
int add = 0;//进位数
for (int j = 0; j < m; j++)//此处 j控制 a的位数
{
int p = a.value.at(j) * b.value.at(i) + add;//与加法相似 将个位存入 >=10的部分累加到进位数中
curr.value.push_back(p % 10);
add = p / 10;
}
while (add != 0)//进位数不为零 则直接将其顺位加入到高位中
{
curr.value.push_back(add % 10);
add /= 10;
}
ret += curr;//累加到结果当中
}
}
ret.zero_justify();
ret.sign = a.sign * b.sign;//最后判断负号
return ret;
}
//重载*=
BigInteger BigInteger::operator *= (const BigInteger& b)
{
*this = *this * b;
return *this;
}
//重载 /
BigInteger operator / (BigInteger a, BigInteger b)
{
if (b == 0)
{
throw "Division by zero condition!";
}//当除数为0时报错
BigInteger ret, row;
ret.sign = a.sign * b.sign;//先判断符号
a.sign = 1;
b.sign = 1;
if(a == b)//两数相等 直接返回1即可
{
ret.value.push_back(1);
return ret;
}
else
{
int i, t;
for (int i = a.value.size() - 1; i >= 0; i--)//从a的最高位开始除
{//如 5214/250 -> 判断 5/250 不够 -> 52/250 不够 -> 521 /250 够 521 / 250 = 2余21 将2加入ret的最低为 row=21 -> 214/250不够 ret左移 得到答案20
row = row << 1;
row.value[0] = a.value[i];
t = 0;// 当除数小于被除数,减去除数,对应位的商增 1
while (b <= row)
{
t++;
row -= b;
}
ret = ret << 1;//在ret=0时 左移时无效的 只有当ret得到第一个不为0的值时才会开始左移
ret.value[0] = t;
}
ret.zero_justify();
}
return ret;
}
//重载/=
BigInteger BigInteger::operator /= (const BigInteger& b)
{
*this = *this / b;
return *this;
}
//重载 =
BigInteger BigInteger::operator = (const BigInteger& b)
{
this->value = b.value;
this->sign = b.sign;
return *this;
}
//重载 %
BigInteger operator % (BigInteger a, BigInteger b)
{//规定答案符号与a相同
BigInteger ret;
int s = a.sign;//此处保存a的符号 方便确定答案的符号
a.sign = 1;
b.sign = 1;
if (a < b)//a<b 答案即为a
{
ret.value = a.value;
}
else if (a == b)//相等 答案为0
{
ret = 0;
}
else
{
BigInteger c;
c = a / b;
ret = a - b * c;
}
if (ret != 0)//如果a的符号为负 ret又为0 则ret的符号改为非负
{
ret.sign = s;
}
return ret;
}
//重载%=
BigInteger BigInteger::operator %= (const BigInteger& b)
{
*this = *this % b;
return *this;
}
//重载^幂运算
BigInteger BigInteger::operator ^ (int n)
{
BigInteger x = *this;
if (n == 0)
{
return BigInteger(1);
}
else if (n == 1)
{
return *this;
}
else if (n == 2)
{
return (*this) * (*this);
}
else
{
for (int i = 1; i < n; i++)
{
*this *= x;
}
}
return *this;
}
递增递减:
//注意返回值的不同影响是否满足链式法则
//后置 递增
BigInteger BigInteger::operator++(int)
{
BigInteger temp;
temp = *this;
*this += 1;
return temp;
}
//后置 递减
BigInteger BigInteger::operator--(int)
{
BigInteger temp;
temp = *this;
*this -= 1;
return temp;
}
//前置 递增
BigInteger& BigInteger::operator++()
{
*this += 1;
return *this;
}
//前置 递减
BigInteger& BigInteger::operator--()
{
*this -= 1;
return *this;
}