C++大整数类BigInteger的四则运算
前言
众所周知,C++是没有高精度运算的,想要用的时候现写结构体非常麻烦,所以这里写出一个库来以后调用比较方便。
这里借用了刘汝佳老师的《算法竞赛入门经典》中的基本写法
接下来的讲解我会将完整代码拆分成部分来讲,完整代码暂时还没有qwq
注:本人开发环境 Visual Studio 2019,所以中间有一些函数是 VS 专用函数
预备知识
-
重载运算符
-
C++ STL vector
-
头文件的写法
-
最基本的面向对象
正文
头文件:BigInteger.h
引用其他头文件部分
#pragma once
#include <cstdio>
#include <iostream>
#include <istream>
#include <ostream>
#include <vector>
#include <cmath>
#include <algorithm>
using namespace std;
大整数类的初始化
这一步是将大整数的初值初始化为0
class BigInteger
{
public:
BigInteger(long long num = 0) { *this = num; };
大整数类的私有成员
这里的BASE记录的是进制的信息,这里 10000 就是指 10000 进制,通俗一点来说,就是把每个数当成一个字符串,从最后一位(即个位)每四位切割成一个数,并以整型变量的方式存储,例:
vector <int> s; //定义一个名称为s的vector便于举例
原数 | 拆分后的vector存储方式 |
---|---|
1 | s[0] = 1 |
12345 | s[0] = 2345, s[1] = 1 |
10010 | s[0] = 10, s[1] = 1 |
123456789 | s[0] = 6789, s[1] = 2345, s[2] = 1 |
如果还有不明白的小伙伴,可以把我的程序拿到本地跑一跑试试,这里不再过多列举了
private:
static const int BASE = 10000;
static const int WIDTH = 4;
vector <int> s;
bool negetive = 0;
大整数类的公有成员(多为重载运算符和函数)
输入输出运算符的重载
public:
friend ostream& operator << (ostream& out, const BigInteger& x);
friend istream& operator >> (istream& in, BigInteger& x);
逻辑运算符,赋值运算符,算术运算符,组合运算符的重载
这里有两类运算符重载,第一类是两个大整数类之间的运算符重载,第二类是一个大整数和一个整型变量之间的运算符重载,第二类的思路是将整型变量转换为大整数类,然后再执行第一类的运算
bool operator < (const BigInteger& b) const;
bool operator > (const BigInteger& b) const;
bool operator <= (const BigInteger& b) const;
bool operator >= (const BigInteger& b) const;
bool operator != (const BigInteger& b) const;
bool operator == (const BigInteger& b) const;
bool operator < (const int& b) const;
bool operator > (const int& b) const;
bool operator <= (const int& b) const;
bool operator >= (const int& b) const;
bool operator != (const int& b) const;
bool operator == (const int& b) const;
BigInteger operator = (long long num);
BigInteger operator = (const string& str);
BigInteger operator + (const BigInteger& b) const;
BigInteger operator - (const BigInteger& b) const;
BigInteger operator * (const BigInteger& b) const;
BigInteger operator / (const BigInteger& b) const;
BigInteger operator % (const BigInteger& b) const;
BigInteger operator += (const BigInteger& b);
BigInteger operator -= (const BigInteger& b);
BigInteger operator *= (const BigInteger& b);
BigInteger operator /= (const BigInteger& b);
BigInteger operator %= (const BigInteger& b);
BigInteger operator + (const int& b) const;
BigInteger operator - (const int& b) const;
BigInteger operator * (const int& b) const;
BigInteger operator / (const int& b) const;
BigInteger operator % (const int& b) const;
BigInteger operator += (const int& b);
BigInteger operator -= (const int& b);
BigInteger operator *= (const int& b);
BigInteger operator /= (const int& b);
BigInteger operator %= (const int& b);
一些比较杂碎的函数
函数功能看下面的注释吧
static int to_int(BigInteger b);
//将一个大整数类转换为一个整型变量,如果精度丢失会直接报错
static BigInteger quick_pow(BigInteger x, BigInteger y, BigInteger mod);
//快速幂,即计算 x^y % mod
static BigInteger gcd(BigInteger x, BigInteger y);
//计算两个大整数类的最大公约数
static BigInteger lcm(BigInteger x, BigInteger y);
//计算两个大整数类的最小公倍数
};
源文件:BigInteger.cpp
与头文件对应:引用其他头文件部分
#include <vector>
#include <istream>
#include <ostream>
#include <algorithm>
#include <cmath>
#include "BigInteger.h"
using namespace std;
与头文件对应:大整数类的公有成员(多为重载运算符和函数)
与头文件对应:输入输出运算符的重载
这里会涉及一些稍微底层的知识,就不详细介绍了(因为我也不会)
ostream& operator << (ostream& out, const BigInteger& x)
{
out << x.s.back();
for (int i = x.s.size() - 2; i >= 0; --i)
{
char buf[20];
sprintf_s(buf, "%04d", x.s[i]);
for (int j = 0; j < strlen(buf); ++j)
out << buf[j];
}
return out;
}
istream& operator >> (istream& in, BigInteger& x)
{
string s;
if (!(in >> s)) return in;
x = s;
return in;
}
与头文件对应:逻辑运算符,赋值运算符,算术运算符,组合运算符的重载
由于我非常懒,这里没有对代码进行分块(我错了…,过后会分的)大家自己费点劲,去下面的代码里自己找符号叭
逻辑运算符的重载
我们先来写小于号 ( < ) 的重载,思路如下:
两个大整数类,先比较位数,如果位数不同,那么大小自然明了;如果位数相同,那么按照位数从高位开始一位一位进行比较
写完后我们会发现,其他所有的逻辑运算符都可以用小于号的一定逻辑来表示,所以其他的函数就非常简单了(这里没有看懂我说的的同学去看下代码吧,应该还比较好理解)
赋值运算符的重载
这里直接读入,几位读入一个数,然后直接存到vector中的一位中去
算术运算符的重载
这里我也会说的很简略,因为只要会模拟这里就不难
四则运算的竖式大家都会吧,这里四则运算都可以直接模拟竖式的行为
组合运算符的重载
直接把赋值运算符和算术运算符组合起来就好啦
bool BigInteger::operator < (const BigInteger& b) const
{
if (s.size() != b.s.size()) return s.size() < b.s.size();
for (int i = s.size() - 1; i >= 0; --i)
if (s[i] != b.s[i]) return s[i] < b.s[i];
return false;
}
bool BigInteger::operator > (const BigInteger& b) const
{
return b < *this;
}
bool BigInteger::operator <= (const BigInteger& b) const
{
return !(b < *this);
}
bool BigInteger::operator >= (const BigInteger& b) const
{
return !(*this < b);
}
bool BigInteger::operator != (const BigInteger& b) const
{
return b < *this || *this < b;
}
bool BigInteger::operator == (const BigInteger& b) const
{
return !(b < *this) && !(*this < b);
}
BigInteger BigInteger::operator = (long long num)
{
s.clear();
do
{
s.push_back(num % BASE);
num /= BASE;
} while (num > 0);
return *this;
}
BigInteger BigInteger::operator = (const string& str)
{
s.clear();
int x, len = (str.length() - 1) / WIDTH + 1;
for (int i = 0; i < len; ++i)
{
int end = str.length() - i * WIDTH;
int start = max(0, end - WIDTH);
sscanf_s(str.substr(start, end - start).c_str(), "%d", &x);
s.push_back(x);
}
return *this;
}
BigInteger BigInteger::operator + (const BigInteger& b) const
{
BigInteger c;
c.s.clear();
for (int i = 0, g = 0; ; ++i)
{
if (g == 0 && i >= s.size() && i >= b.s.size()) break;
int x = g;
if (i < s.size()) x += s[i];
if (i < b.s.size()) x += b.s[i];
c.s.push_back(x % BASE);
g = x / BASE;
}
return c;
}
BigInteger BigInteger::operator - (const BigInteger& b) const
{
BigInteger c;
c.s.clear();
int MAX = max(s.size(), b.s.size());
if (*this > b)
{
for (int i = 0, g = 0; ; ++i)
{
bool borrow = 0;
if (g == 0 && i >= MAX) break;
int x = g;
if (i < s.size()) x += s[i];
if (i < b.s.size()) x -= b.s[i];
if (x < 0) borrow = 1;
c.s.push_back((x + BASE) % BASE);
g = x / BASE;
if (borrow) --g;
}
return c;
}
else if (*this == b)
{
return c;
}
else
{
for (int i = 0, g = 0; ; ++i)
{
bool borrow = 0;
if (g == 0 && i >= MAX) break;
int x = g;
if (i < b.s.size()) x += b.s[i];
if (i < s.size()) x -= s[i];
if (x < 0) borrow = 1;
if (i == MAX - 1)
c.s.push_back(-x % BASE);
else
c.s.push_back((x + BASE) % BASE);
g = x / BASE;
if (borrow) --g;
}
return c;
}
}
BigInteger BigInteger::operator * (const BigInteger& b) const
{
BigInteger c;
c.s.clear();
if (s.size() == 1 && s[0] == 0)
{
c.s.clear();
c.s.push_back(0);
return c;
}
if (b.s.size() == 1 && b.s[0] == 0)
{
c.s.clear();
c.s.push_back(0);
return c;
}
int MAX = max(s.size(), b.s.size());
for (int i = 0; i < (MAX * 2 + 2); ++i)
c.s.push_back(0);
for (int i = 0; i < s.size(); ++i)
for (int j = 0; j < b.s.size(); ++j)
{
c.s[i + j + 1] += c.s[i + j] / BASE;
c.s[i + j] %= BASE;
c.s[i + j] += s[i] * b.s[j];
}
for (int i = 0; i < (MAX * 2 + 1); ++i)
{
c.s[i + 1] += c.s[i] / BASE;
c.s[i] %= BASE;
}
while (!*(c.s.end() - 1))
c.s.erase(c.s.end() - 1);
return c;
}
BigInteger BigInteger::operator / (const BigInteger& b) const
{
if (b.s.size() == 1 && b.s[0] == 0)
{
cerr << "Exception has occured." << endl;
cerr << "Arithmetic exception: Divided by Zero." << endl;
exit(1);
}
BigInteger c;
c.s.clear();
BigInteger substraction = *this;
BigInteger tmp;
tmp.s.clear();
tmp.s.push_back(1);
if (substraction > b)
{
while (substraction >= b)
{
substraction -= b;
if (substraction.s.size() > 1)
while (!*(substraction.s.end() - 1))
substraction.s.erase(substraction.s.end() - 1);
c += tmp;
}
}
else if (substraction == b)
c.s.push_back(1);
else
c.s.push_back(0);
return c;
}
BigInteger BigInteger::operator % (const BigInteger& b) const
{
BigInteger c;
c = *this - (*this / b) * b;
return c;
}
BigInteger BigInteger::operator += (const BigInteger& b)
{
*this = *this + b;
return *this;
}
BigInteger BigInteger::operator -= (const BigInteger& b)
{
*this = *this - b;
return *this;
}
BigInteger BigInteger::operator *= (const BigInteger& b)
{
*this = *this * b;
return *this;
}
BigInteger BigInteger::operator /= (const BigInteger& b)
{
*this = *this / b;
return *this;
}
BigInteger BigInteger::operator %= (const BigInteger& b)
{
*this = *this % b;
return *this;
}
BigInteger BigInteger::fast_pow(BigInteger x, BigInteger y)
{
BigInteger ans = 1;
while (y > 0)
{
if (y.s[0] & 1)
ans *= x;
x *= x;
y /= 2;
}
return ans;
}