上次写了一个“无符号大整数加法”,是比较容易的,这次实现了完整的大整数的加减法,支持有符号的!不过实现起来感觉不是很顺畅,感觉可以优化的地方还很多,先贴一下代码,日后再来优化。
另,思路主要是模拟手算的过程,计算方式在注释里有说清楚。
// BigInteger.h
#ifndef __BIG_INTEGER__H__
#define __BIG_INTEGER__H__
#include
using namespace std;
class BigInteger {
public:
BigInteger(string str);
BigInteger add(const BigInteger& other);
BigInteger sub(const BigInteger& other);
friend ostream& operator << (ostream& out, BigInteger& num);
private:
string m_data;
bool m_isNeg;
private:
static void align(string& str, int len);
static bool strip(string& str);
static bool isOKChar(char ch);
static BigInteger addAbsoluteValue(const BigInteger& A, const BigInteger& B);
static BigInteger subAbsoluteValue(const BigInteger& A, const BigInteger& B);
static int compareAbsoluteValue(const BigInteger& A, const BigInteger& B);
};
#endif
// BigInteger.cpp
#include "BigInteger.h"
#include <assert.h>
#include
using namespace std;
// 使一个表示数字的字符串对齐到len的长度,方便后续位对位的运算
void BigInteger::align(string& str, int len) {
if (len > str.size())
str.insert(0, len - str.size(), '0');
}
// 判断字符是否是合法的,即是否为数字或+-号
bool BigInteger::isOKChar(char ch) {
return (ch >= '0' && ch <= '9') || (ch == '+') || (ch == '-');
}
// 使得字符串str合法化,并返回其正负符号
bool BigInteger::strip(string& str) {
int left = 0, right = str.size() - 1;
// 去除前缀中非法的字符
while (left <= right && !isOKChar(str[left]))
++left;
// 看看是否有符号
bool isNeg = false;
if (str[left] == '-' || str[left] == '+') {
isNeg = str[left] == '-';
++left;
}
// 去掉前缀多余的0
while (left <= right && str[left] == '0')
++left;
// 去除后缀里非法的字符
while (right >= left && !isOKChar(str[right]))
--right;
// 如果这个数字是0,那么应该保留一个0
if (left > right)
str = "0";
else if (left != 0 || right != str.size() - 1)
str = str.substr(left, right - left + 1);// 有改变才取子串
return isNeg;
}
BigInteger::BigInteger(string str)
: m_data(str), m_isNeg(false) {
m_isNeg = strip(m_data);
}
// 比较两个大整数的绝对值大小
int BigInteger::compareAbsoluteValue(const BigInteger& A, const BigInteger& B) {
if (A.m_data == B.m_data)
return 0;
// 对数据的copy做处理
string dataA = A.m_data, dataB = B.m_data;
int len = max(dataA.size(), dataB.size()), borrow = 0;
BigInteger::align(dataA, len);
BigInteger::align(dataB, len);
// 模拟手算的减法
for (int i = len - 1; i >= 0; --i) {
int delta = dataA[i] - dataB[i] - borrow;
borrow = 0;
while (delta < 0) {
delta += 10;
++borrow;
}
}
// 如果最后有借位,说明被减数是小于减数的!
return (borrow == 0) ? 1 : -1;
}
// 调用这个函数,必须保证:A的绝对值比B的大
BigInteger BigInteger::subAbsoluteValue(const BigInteger& A, const BigInteger& B) {
string dataA = A.m_data, dataB = B.m_data;
int len = max(dataA.size(), dataB.size()), borrow = 0;
BigInteger::align(dataA, len);
BigInteger::align(dataB, len);
string result(len, '0');
for (int i = len-1; i >= 0; --i) {
int delta = dataA[i] - dataB[i] - borrow;
borrow = 0;
while (delta < 0) {
delta += 10;
++borrow;
}
result[i] += delta;
}
// 最后断言一下:要满足A的绝对值比B的大
assert(borrow == 0);
return BigInteger(result);
}
// 直接将两个数字的绝对值相加
BigInteger BigInteger::addAbsoluteValue(const BigInteger& A, const BigInteger& B) {
string dataA = A.m_data, dataB = B.m_data;
int len = max(dataA.size(), dataB.size());
BigInteger::align(dataA, len);
BigInteger::align(dataB, len);
string result(len, '0');
int toAdd = 0;
for (int i = len-1; i >= 0; --i) {
int sum = (dataA[i] - '0') + (dataB[i] - '0') + toAdd;
toAdd = sum / 10;
result[i] += sum % 10;
}
// 处理最后的进位
if (toAdd > 0)
result.insert(result.begin(), toAdd+'0');
return BigInteger(result);
}
// 大整数加法
BigInteger BigInteger::add(const BigInteger& other) {
// 加法:同号,结果=绝对值相加,符号取相同的符号
if (m_isNeg == other.m_isNeg) {
BigInteger result = addAbsoluteValue(*this, other);
result.m_isNeg = m_isNeg;
return result;
} else {
// 加法:异号,结果=大的绝对值-小的绝对值,符号取绝对值大的那个
if (compareAbsoluteValue(*this, other) >= 0) {
BigInteger result = subAbsoluteValue(*this, other);
result.m_isNeg = m_isNeg;
return result;
} else {
BigInteger result = subAbsoluteValue(other, *this);
result.m_isNeg = other.m_isNeg;
return result;
}
}
}
// 大整数减法
BigInteger BigInteger::sub(const BigInteger& other) {
// 减法:异号,结果=绝对值相加,符号取被减数的符号
if (m_isNeg != other.m_isNeg) {
BigInteger result = addAbsoluteValue(*this, other);
result.m_isNeg = m_isNeg;
return result;
} else {
// 减法:同号,结果=大的绝对值-小的绝对值,符号取绝对值大的那个
if (compareAbsoluteValue(*this, other) >= 0) {
BigInteger result = subAbsoluteValue(*this, other);
result.m_isNeg = m_isNeg;
return result;
} else {
BigInteger result = subAbsoluteValue(other, *this);
result.m_isNeg = !m_isNeg;
return result;
}
}
}
// 输出大整数,包括符号位
ostream& operator << (ostream& out, BigInteger& num) {
if (num.m_isNeg) {
string pre(1, '-');
out << pre;
}
out << num.m_data;
return out;
}
// main.cpp
// 不懂规范化的测试是硬伤
#include
#include "BigInteger.h"
using namespace std;
int main() {
BigInteger A("+00123"), B(" -0666 "), C("9"), D("111"), E("1000"), F("101"), G("999"), H("0");
BigInteger bi2 = C.add(D), bi3 = D.add(C), bi4 = F.sub(D), bi5 = E.sub(C), bi6 = G.add(D), bi7 = H.sub(G);
cout << bi2 << ' ' << bi3 << ' ' << bi4 << ' ' << bi5 << ' ' << bi6 << ' ' << bi7 << endl;
BigInteger bi8 = A.sub(A);
cout << bi8 << endl;
return 0;
}
objs = main.o BigInteger.o
CC = g++
out: $(objs)
$(CC) -o out $(objs)
main.o: main.cpp
BigInteger.o: BigInteger.h
clean:
rm *.o out