大整数运算
简述
创建HugeInteger(大整数)类,用一个具有40个元素的数字数组存储最多40位的整数,提供基本的关系运算、构造函数以及加减乘除等运算。为了简化代码,我们在这个程序中假设所有的大整数都是正数。
类的基本成员
类的数据成员主要是两种,分别是一个用于存储大整数的数组和一个表示大整数容量的静态常量。成员函数主要有以long long为形参和以string为形参的构造函数,以及重载的输出运算符、加减乘除运算符和相关的逻辑关系运算符。类的基本成员声明如下:
class HugeInteger {
friend ostream& operator<<(ostream& os, const HugeInteger&); // 重载输出运算符
public:
// 基本函数
HugeInteger(long long = 0);
HugeInteger(const string&);
HugeInteger(const HugeInteger&);
HugeInteger& operator=(const HugeInteger&);
// 加减乘除运算
const HugeInteger operator+(const HugeInteger&) const;
const HugeInteger operator-(const HugeInteger&) const;
const HugeInteger operator*(int) const; // 与整型的乘法
const HugeInteger operator*(const HugeInteger&) const; // 与大整数的乘法
const HugeInteger operator/(int) const; // 与整型的除法
// 逻辑关系运算
bool operator==(const HugeInteger&) const;
bool operator!=(const HugeInteger&) const;
bool operator>(const HugeInteger&) const;
bool operator<(const HugeInteger&) const;
bool operator>=(const HugeInteger&) const;
bool operator<=(const HugeInteger&) const;
private:
static const int SIZE = 40; // 存储大整数的最大容量
int number[SIZE]; // 存储大整数的数组
};
相关成员函数的实现
构造函数及赋值函数
类的构造函数有使用long long作为形参和以string作为形参的两种构造函数,故我们可以考虑只实现其中string类型的构造函数,而把long long形参的构造函数的构造工作委托给string形参的构造函数,保证了代码整体的统一性。
HugeInteger::HugeInteger(long long n) : HugeInteger(to_string(n)) {} // 委托其他构造函数初始化
HugeInteger::HugeInteger(const string& s) {
fill(begin(number), end(number), 0); // 清零
for (int i = s.size() - 1, j = SIZE - 1; i >= 0; --i, --j) // 将string中的数字存储到类中数组
number[j] = s[i] - '0';
}
赋值构造函数和赋值运算符函数
赋值构造函数和赋值运算符函数的主要作用就是将函数参数中的数据赋值给this中的数组,涉及到数组元素的赋值,没有特殊的要求,直接扫描整个数组逐一赋值即可。
HugeInteger::HugeInteger(const HugeInteger& h) {
fill(begin(number), end(number), 0); // 清零
for (size_t i = 0; i < SIZE; ++i)
number[i] = h.number[i];
}
HugeInteger& HugeInteger::operator=(const HugeInteger& h) {
for (size_t i = 0; i < SIZE; ++i)
number[i] = h.number[i];
return *this;
}
加法运算
加法运算从低位开始逐位相加,但需要注意进位问题,相邻两个位置可能会有进位,故我们除了对相应位置上的两个数字相加,还需要再额外加上进位数据,得到的结果的个位数作为该位的结果,十位数作为进位。
const HugeInteger HugeInteger::operator+(const HugeInteger& h) const {
int carry = 0; // 保存进位数据
HugeInteger temp; // 存储结果
for (int i = SIZE - 1; i >= 0; --i) { // 从个位开始扫描
temp.number[i] = number[i] + h.number[i] + carry; // 注意加上进位
carry = temp.number[i] / 10; // 取十位作为进位
temp.number[i] %= 10; // 取个位作为当前位
}
if (carry != 0) { // 有进位没有处理完,溢出
cerr << "Over Flow!" << endl;
exit(EXIT_FAILURE);
}
return temp;
}
减法运算
由于我们没有考虑负数的运算,而减法可能产生负数,因此我们在减法开始前,先判断被减数与减数的运算是否会产生负数,如果会产生负数,则报错。在保证被减数大于或等于减数的前提下,我们也是从低位开始进行计算,比较被减位置和减位,如果不够减,则需要像高位借1,借来的1到了低位则加10,然后就能够进行正常的减法运算。
const HugeInteger HugeInteger::operator-(const HugeInteger& h) const {
HugeInteger tem, lhs = *this; // 缓存左边操作数
if (*this < h) { // 如果减法会产生错误则报错
cerr << "The left value is smaller!\n";
return tem;
}
for (int i = SIZE - 1; i >= 0; --i) { // 低位开始相减
if (lhs.number[i] < h.number[i]) { // 如果不够减则借位
--lhs.number[i - 1];
lhs.number[i] += 10;
}
tem.number[i] = lhs.number[i] - h.number[i];
}
return tem;
}
乘法运算
首先我们考虑HugeInteger与普通int类型的乘法,int类型变量之间的乘法本身是语言自身支持的,因此我们可以考虑把左操作数的HugeInteger中的数字逐位与右操作数int类型的变量整体相乘,再与进位相加,所得结果的个位数作为当前位的结果,高位部分作为新的进位参与下一次运算。
const HugeInteger HugeInteger::operator*(int rhs) const {
HugeInteger result;
int carry = 0;
for (int i = SIZE - 1; i >= 0; --i) { // 逐位与rhs相乘
int temp = number[i] * rhs + carry; // 把lhs的一位与rhs的整体相乘
carry = temp / 10; // 保留进位
result.number[i] = temp % 10; // 留在当前位
}
if (carry != 0) { // 有进位没有处理完,溢出
cerr << "Over Flow!" << endl;
exit(EXIT_FAILURE);
}
return result;
}
然后以同样是思路来设计HugeInteger变量与HugeInteger变量之间的乘法,同样,我们把右操作数拆分,把它看成是一个一个的int类型,因此我们可以对右操作数逐个位使用HugeInteger与int类型之间的乘法,简化代码,这里需要注意一点就是乘出来的结果需要把后置的0算上,例如\((123 * 100)\),把右边的操作数拆分后逐位相乘,个位和十位的0的结果当然是0,而百位的\(123 * 1 = 123\),得出结果后需要把百位的\(10^2\)算上,得出\(123 * 10^2 = 12300\)。具体看代码实现:
const HugeInteger HugeInteger::operator*(const HugeInteger& h) const {
HugeInteger result;
for (int i = SIZE - 1, times = 0; i >= 0; --i, ++times) {
HugeInteger temp = *this * h.number[i]; // 转换为与整形的乘法
for (int time = 0; time < times; ++time) // 处理后置的0的位数
temp = temp * 10;
result = result + temp; // 当前位置的运算结果加入最终结果
}
return result;
}
除法运算
在这里我们暂时设计的是HugeInteger类型与整型类型的除法。回顾我们平常列除法竖式的步骤,首先将上一步得来的余数乘10加上当前位(如果是首位,则上一步的余数为0)得到当前的被除数。把这个被除数与除数相比较,如果不够除,则当前位商0;如果够除,则商为当前位的商,余数参与下一位的运算(最后一步的余数舍去)。
const HugeInteger HugeInteger::operator/(int rhs) const {
HugeInteger result;
int pos = 0;
while (number[pos] == 0) // 跳过高位的0
++pos;
int r = 0; // 保存余数
for (; pos < SIZE; ++pos) {
r = r * 10 + number[pos]; // 当前位待除的数
if (r < rhs) // 如果不够除,商0
result.number[pos] = 0;
else { // 够除
result.number[pos] = r / rhs;
r %= rhs; // 余数参与下次运算
}
}
return result;
}
逻辑关系运算
逻辑关系运算中,主要需要注意的就是在实现了一种运算后,其他运算应该要尽可能地使用已经实现的运算完成,简化代码。
bool HugeInteger::operator<(const HugeInteger& h) const {
for (size_t i = 0; i < SIZE; ++i) // 从高位开始逐位相比
if (number[i] < h.number[i])
return true;
else if (number[i] > h.number[i])
return false;
return false;
}
bool HugeInteger::operator>(const HugeInteger& h) const {
return h < *this;
}
bool HugeInteger::operator==(const HugeInteger& h) const {
for (size_t i = 0; i < SIZE; ++i) // 从高位开始逐位相比
if (number[i] != h.number[i])
return false;
return true;
}
bool HugeInteger::operator!=(const HugeInteger& h) const {
return !(*this == h);
}
bool HugeInteger::operator<=(const HugeInteger& h) const {
return!(*this > h);
}
bool HugeInteger::operator>=(const HugeInteger& h) const {
return !(*this < h);
}
输出运算符
HugeInteger类输出时,按数字从高位到低位逐位输出即可,唯一需要注意的是,高位为0的情况,高位为0的位不需要输出,因此跳过前导的0,这里有一个特判,如果所有的位都为0,则需要输出一个0,否则按位输出。
ostream& operator<<(ostream & os, const HugeInteger & h) {
size_t i = 0;
while (i < h.SIZE && h.number[i] == 0)
++i;
if (i == h.SIZE)
os << 0;
else {
while (i < h.SIZE)
os << h.number[i++];
}
return os;
}
完整代码
#include <iostream>
#include <string>
#include <algorithm>
using namespace std;
class HugeInteger {
friend ostream& operator<<(ostream& os, const HugeInteger&); // 重载输出运算符
public:
// 基本函数
HugeInteger(long long = 0);
HugeInteger(const string&);
HugeInteger(const HugeInteger&);
HugeInteger& operator=(const HugeInteger&);
// 加减乘除运算
const HugeInteger operator+(const HugeInteger&) const;
const HugeInteger operator-(const HugeInteger&) const;
const HugeInteger operator*(int) const; // 与整型的乘法
const HugeInteger operator*(const HugeInteger&) const; // 与大整数的乘法
const HugeInteger operator/(int) const; // 与整型的除法
// 逻辑关系运算
bool operator==(const HugeInteger&) const;
bool operator!=(const HugeInteger&) const;
bool operator>(const HugeInteger&) const;
bool operator<(const HugeInteger&) const;
bool operator>=(const HugeInteger&) const;
bool operator<=(const HugeInteger&) const;
private:
static const int SIZE = 40; // 存储大整数的最大容量
int number[SIZE]; // 存储大整数的数组
};
ostream& operator<<(ostream& os, const HugeInteger& h) {
size_t i = 0;
while (i < h.SIZE && h.number[i] == 0)
++i;
if (i == h.SIZE)
os << 0;
else {
while (i < h.SIZE)
os << h.number[i++];
}
return os;
}
HugeInteger::HugeInteger(long long n) : HugeInteger(to_string(n)) {} // 委托其他构造函数初始化
HugeInteger::HugeInteger(const string& s) {
fill(begin(number), end(number), 0); // 清零
for (int i = s.size() - 1, j = SIZE - 1; i >= 0; --i, --j) // 将string中的数字存储到类中数组
number[j] = s[i] - '0';
}
HugeInteger::HugeInteger(const HugeInteger& h) {
fill(begin(number), end(number), 0); // 清零
for (size_t i = 0; i < SIZE; ++i)
number[i] = h.number[i];
}
HugeInteger& HugeInteger::operator=(const HugeInteger& h) {
for (size_t i = 0; i < SIZE; ++i)
number[i] = h.number[i];
return *this;
}
const HugeInteger HugeInteger::operator+(const HugeInteger& h) const {
int carry = 0; // 保存进位数据
HugeInteger temp; // 存储结果
for (int i = SIZE - 1; i >= 0; --i) { // 从个位开始扫描
temp.number[i] = number[i] + h.number[i] + carry; // 注意加上进位
carry = temp.number[i] / 10; // 取十位作为进位
temp.number[i] %= 10; // 取个位作为当前位
}
if (carry != 0) { // 有进位没有处理完,溢出
cerr << "Over Flow!" << endl;
exit(EXIT_FAILURE);
}
return temp;
}
const HugeInteger HugeInteger::operator-(const HugeInteger& h) const {
HugeInteger temp, lhs = *this; // 缓存左边操作数
if (*this < h) { // 如果减法会产生错误则报错
cerr << "The left value is smaller!\n";
return temp;
}
for (int i = SIZE - 1; i >= 0; --i) { // 低位开始相减
if (lhs.number[i] < h.number[i]) { // 如果不够减则借位
--lhs.number[i - 1];
lhs.number[i] += 10;
}
temp.number[i] = lhs.number[i] - h.number[i];
}
return temp;
}
const HugeInteger HugeInteger::operator*(int rhs) const {
HugeInteger result;
int carry = 0;
for (int i = SIZE - 1; i >= 0; --i) { // 逐位与rhs相乘
int temp = number[i] * rhs + carry; // 把lhs的一位与rhs的整体相乘
carry = temp / 10; // 保留进位
result.number[i] = temp % 10; // 留在当前位
}
if (carry != 0) { // 有进位没有处理完,溢出
cerr << "Over Flow!" << endl;
exit(EXIT_FAILURE);
}
return result;
}
const HugeInteger HugeInteger::operator*(const HugeInteger& h) const {
HugeInteger result;
for (int i = SIZE - 1, times = 0; i >= 0; --i, ++times) {
HugeInteger temp = *this * h.number[i]; // 转换为与整形的乘法
for (int time = 0; time < times; ++time) // 处理后置的0的位数
temp = temp * 10;
result = result + temp; // 当前位置的运算结果加入最终结果
}
return result;
}
const HugeInteger HugeInteger::operator/(int rhs) const {
HugeInteger result;
int pos = 0;
while (number[pos] == 0) // 跳过高位的0
++pos;
int r = 0; // 保存余数
for (; pos < SIZE; ++pos) {
r = r * 10 + number[pos]; // 当前位待除的数
if (r < rhs) // 如果不够除,商0
result.number[pos] = 0;
else { // 够除
result.number[pos] = r / rhs;
r %= rhs; // 余数参与下次运算
}
}
return result;
}
bool HugeInteger::operator<(const HugeInteger& h) const {
for (size_t i = 0; i < SIZE; ++i) // 从高位开始逐位相比
if (number[i] < h.number[i])
return true;
else if (number[i] > h.number[i])
return false;
return false;
}
bool HugeInteger::operator>(const HugeInteger& h) const {
return h < *this;
}
bool HugeInteger::operator==(const HugeInteger& h) const {
for (size_t i = 0; i < SIZE; ++i) // 从高位开始逐位相比
if (number[i] != h.number[i])
return false;
return true;
}
bool HugeInteger::operator!=(const HugeInteger& h) const {
return !(*this == h);
}
bool HugeInteger::operator<=(const HugeInteger& h) const {
return!(*this > h);
}
bool HugeInteger::operator>=(const HugeInteger& h) const {
return !(*this < h);
}
int main() {
HugeInteger n1(7654321);
HugeInteger n2("100000000000000");
HugeInteger n3("12341234");
HugeInteger n4("7888");
HugeInteger n;
n = n1 + n2;
cout << n1 << " + " << n2 << " = " << n << endl;
n = n2 - n1;
cout << n2 << " - " << n1 << " = " << n << endl;
n = n3 * n4;
cout << n3 << " * " << n4 << " = " << n << endl;
n = n3 / 7888;
cout << n3 << " / " << 7888 << " = " << n << endl;
return 0;
}