标签:C++ 高精度加法 高精度减法
by 小威威
在学C语言的时候,我有实现过高精度乘法(想了解的戳它)。
其实高精度加法、减法比乘法简单多了,所以可以通过高精度乘法这篇文章来入门高精度的算法。
现在是用C++实现,显然有了string类,一切的操作都变的简单多了。
题目如下:(Author: 欧文杰(TA))
This is an easy big integer. Your task is to finish the class big integer:
Tips:
size_ means the size of the big integer
friend function should have been declared outside;
when you declare the friend function, you should forward declare the class.
when you cannot compile, try to read the informations or google it.
overload “<<” : please output like this format: “123456”, no wrap.
overload “-“: we promise that a >= b when a - b .
其中,main.cpp 与 BigInteger.h已给出。
\\ main.cpp
#include "BigInteger.h"
#include
#include
using namespace std;
int main() {
string s1, s2, s3, s4;
cin >> s1 >> s2;
cin >> s4;
BigInteger a(s1);
BigInteger b(s2);
BigInteger c(a);
BigInteger d(s4);
BigInteger sum(a+b);
BigInteger diff(c-d);
cout << sum << endl;
cout << diff << endl;
return 0;
}
\\ BigInteger.h
#ifndef _BIG_INTEGER_
#define _BIG_INTEGER_
#include
#include
using std::string;
using std::ostream;
// forward declaration
class BigInteger;
// operators overloading
BigInteger operator+(const BigInteger& left, const BigInteger& right);
BigInteger operator-(const BigInteger& left, const BigInteger& right);
ostream& operator<<(ostream& out, const BigInteger& Bint);
class BigInteger {
public:
BigInteger();
explicit BigInteger(const string& number);
BigInteger(const BigInteger& other);
friend BigInteger operator+(const BigInteger& left,
const BigInteger& right);
// promise left >= right
friend BigInteger operator-(const BigInteger& left,
const BigInteger& right);
friend ostream& operator<<(ostream& out, const BigInteger& Bint);
~BigInteger();
private:
int *data_;
int size_;
};
#endif // _BIG_INTEGER_
现在我主要来讲解一下如何实现高精度加法与减法。
先交代一下背景,我们用string对象接受a,b,然后再将它们存储到数组中进行高精度的操作。
实现高精度加法,我们可以先将整个数组看成一个很大的数字,其中数组的每个元素相当于这个数字的一个位。那么实现这两个很大的数字的加法可以采用“先对应位相加,后进行进位操作”的策略。
先来谈谈对应位相加。要实现对应位相加,也就是实现下标相同的数组元素的相加。这时要注意了,两个数字的位数不同,故数组的元素数目也不同,如果只是简单的用for循环实现数组对应位相加的话,会产生错误,因为数组可能会出现越界引发不可预估的错误。这种情况下,说明要先判断两个数据的位数大小,然后取max_size(a,b)+1作为存储结果的数组大小(加1的原因是加法可能会产生进位,从而使数据的位数增多)。所以在相加之前,我们要先判断大小。接着,又有一个问题。数字相加是对应位相加,如果我们数组按照高位到低位的形式存储数据,那么会导致对应位的相加不那么方便,因为对应位的数组元素的坐标不同。因此我们可以选择将数据倒着存储,这样就可以用相同下标的数组元素相加来实现对应位的相加。
再谈谈进位操作。进位操作最好是在对应位相加完之后再进行,因为我们采用的相加顺序是从低位到高位,倘若你在对应位相加后进行进位操作,那么下一个对应位相加便会覆盖结果中对应位的数据导致进位丢失。进位操作很简单,就是一个取余与除法的问题,这里就不再强调了。
对于结果中最高的那一位,有可能存在也有可能不存在,这都是取决于倒数第二位。可以有这样的思路,如果倒数第二位需要进位,我们就将数组允许访问的大小加1(通常我们都是设置一个比较大的数组,然后再用一个成员size来限制数组被访问的区域);如果倒数第二位不需要进位,我们可以对size不进行任何操作。这个思路是标程的做法,所以不会在我待会的代码里出现,在这里仅是扩展一下思路而已。
由于题目的限制,a是>=b的(如果没有这个前提,那就要像上面的加法一样进行判断大小)。实现减法与实现加法的原理差不多,现在主要说明一下与高精度加法的不同点。
对于加法来说,是max_size(a, b)+1,而对于减法是max_size(a, b)。因为不会出现进位操作。然而,虽然没有进位,但是有退位呀,所以会出现结果数组的高位出现无意义0的情况,如000001200(指1200),在这里有两种处理方法:第一种就是修改数组被访问区域的大小,也就是size–,另一种方法就是在输出中进行筛选,自动过滤掉无意义的0。
下面我将呈现个人的代码:
# include "BigInteger.h"
BigInteger :: BigInteger() {
data_ = NULL;
size_ = 0;
}
BigInteger :: BigInteger(const string& number) {
data_ = NULL;
size_ = 0;
data_ = new int[number.size()];
int j = 0;
for (int i = number.size()-1; i >= 0; i--) {
data_[j] = number[i] - '0';
j++;
}
size_ = number.size();
}
BigInteger :: BigInteger(const BigInteger& other) {
data_ = NULL;
size_ = other.size_;
data_ = new int[size_];
for (int i = 0; i < size_; i++) {
data_[i] = other.data_[i];
}
}
BigInteger operator+(const BigInteger& left,
const BigInteger& right) {
BigInteger copy;
if (left.size_ < right.size_) {
copy.size_ = right.size_+1;
copy.data_ = new int[copy.size_];
copy.data_[copy.size_-1] = 0;
for (int i = 0; i < right.size_; i++) {
if (i < left.size_) {
copy.data_[i] = left.data_[i] + right.data_[i];
} else {
copy.data_[i] = right.data_[i];
}
}
for (int i = 0; i < right.size_; i++) {
if (copy.data_[i] >= 10) {
copy.data_[i+1]++;
copy.data_[i] -= 10;
}
}
} else {
copy.size_ = left.size_+1;
copy.data_ = new int[copy.size_];
copy.data_[copy.size_-1] = 0;
for (int i = 0; i < left.size_; i++) {
if (i < right.size_) {
copy.data_[i] = left.data_[i] + right.data_[i];
} else {
copy.data_[i] = left.data_[i];
}
}
for (int i = 0; i < left.size_; i++) {
if (copy.data_[i] >= 10) {
copy.data_[i+1]++;
copy.data_[i] -= 10;
}
}
}
return copy;
}
// promise left >= right
BigInteger operator-(const BigInteger& left,
const BigInteger& right) {
BigInteger copy;
copy.size_ = left.size_;
copy.data_ = new int[left.size_];
for (int i = 0; i < left.size_; i++) {
if (i < right.size_) {
copy.data_[i] = left.data_[i] - right.data_[i];
} else {
copy.data_[i] = left.data_[i];
}
}
for (int i = 0; i < left.size_; i++) {
if (copy.data_[i] < 0) {
copy.data_[i+1]--;
copy.data_[i] += 10;
}
}
return copy;
}
ostream& operator<<(ostream& out, const BigInteger& Bint) {
int key = 0;
for (int i = Bint.size_-1; i >= 0; i--) {
if (Bint.data_[i] != 0) key = 1;
if (Bint.data_[i] == 0 && key == 0) continue;
out << Bint.data_[i];
}
int j;
for (j = 0; j < Bint.size_; j++) {
if (Bint.data_[j] != 0) break;
}
if (j == Bint.size_) {
out << 0;
}
return out;
}
BigInteger :: ~BigInteger() {
if (data_ != NULL)
delete[] data_;
}
在这道题有个地方很容易出现内存错误,这个错误我debug了不久才找出来的。就是用new分配一段内存,这段内存在没有初始化的条件下除了执行初始化操作以外的所有操作都是非法访问。这一点要非常注意。
如程序里的实现高精度加法那里:
copy.data_[copy.size_-1] = 0;
这个是对最高位的初始化,如果没有初始化,不能执行++操作,这种操作是非法的。
以上内容皆为本人观点,欢迎大家提出批评和指导,我们一起探讨!