文章目录
- 一、实验目的、内容
- 二、实验程序设计及结构
- 1.需求分析
- 类
- 变量
- 函数
- 2.设计结构或流程图
- 三、设计过程
- 四、测试分析
- 第一组
- 第二组
- 实验中出现的bug及解决方案
- 五、设计的特点和结果
一、实验目的、内容
大数是超过整数表示范围的整数,针对正整数运算,定义一个大数类,并编写两个大数类对象的加法和减法函数。
二、实验程序设计及结构
1.需求分析
类
bigu
,内含p
(unsigned char*
)、n
(size_t
)数据成员及构造函数、析构函数等成员函数。
变量
bigu
类型数据a
和b
,用于测试数据。
函数
主函数、运算符重载函数及bigu
类的成员函数。
2.设计结构或流程图
- 进行
a
和b
的输入。(调用构造函数及istream& operator>>(istream& c, bigu& x)
)- 从输入的第一个数字字符开始,定义
unsigned short
临时数组(看作256进制数)。 - 每输入一个数字字符,每个数组元素乘10。
- 遍历临时数组,将超过255的元素执行进位操作;若数组长度不够,重新定义数组并拷贝。
- 不断输入直到遇到非数字字符为止。
- 将临时数组的数据拷贝进
unsigned char
数组并存储。
- 从输入的第一个数字字符开始,定义
- 进行
a
和b
的加法并输出。(调用bigu operator+(const bigu& x, const bigu& y)
和ostream& operator<<(ostream& c, const bigu& x)
)- 加法运算
- 根据整数运算进位的性质,和的元素个数为
max(x.n,y.n)
或max(x.n,y.n)+1
,故定义max(x.n,y.n)+1
长度的临时数组。 - 按
x.n
和y.n
的大小分类讨论。 - 将临时数组初始化为
x
和y
各位元素之和。 - 遍历临时数组,对值超过255的执行进位操作。
- 将临时数组的数据拷贝进
unsigned char
数组并存储。
- 根据整数运算进位的性质,和的元素个数为
- 输出
- 定义临时数组。
- 从最高位开始,将超过9的数据进行十进制展开。
- 每次加新数之前将原来的所有元素乘256并对超过9的数进行进位。
- 输出并释放临时数组。
- 加法运算
- 比较
a
与b
的大小,作差后输出。(调用bool operator>(const bigu& x, const bigu& y)
、bigu operator-(const bigu& x, const bigu& y)
和ostream& operator<<(ostream& c, const bigu& x)
)- 大于运算
- 若两者元素个数不同,直接返回结果。
- 逐位比较,直到两者不相等为止。
- 减法运算(与加法类似)
- 大于运算
三、设计过程
#include <iostream>
using namespace std;
class bigu
{
public:unsigned char *p; // 指向正整数数组size_t n; // 数组长度bigu() { *(p = new unsigned char[n = 1]) = '\0'; } // 默认构造函数:初始化为0// 带参数构造(无符号整型)bigu(unsigned long long x){unsigned char a[8], *q(a), *t(q);*q = x;while (x >>= 8)*++q = x; // 逐字节读取*(p = new unsigned char[n = ++q - t]) = *t;while (++t < q)*++p = *t;++(p -= n);}// 为了防止浅拷贝的问题,重写拷贝构造函数bigu(const bigu &x){delete[] p; // 防止内存泄漏const unsigned char *q(x.p);*(p = new unsigned char[n = x.n]) = *q;while (--n)*++p = *++q;++(p -= n = x.n);}~bigu() { delete[] p; } // 析构函数// 为了加快效率、避免拷贝的有参构造函数bigu(unsigned char *a, size_t b){p = a;n = b;}
};
// 重载关系运算符
bool operator>(const bigu &x, const bigu &y)
{if (x.n > y.n)return true;if (y.n > x.n)return false;const unsigned char *p(x.p + x.n), *q(y.p + y.n);doif (*--p > *--q)return true;else if (*p < *q)return false;while (p > x.p);return false;
}
// 重载加法运算符
bigu operator+(const bigu &x, const bigu &y)
{const unsigned char *p(x.p), *q(y.p); // 读取数据// 考虑新数组大小将会是x,y中较大的那个或是较大的加1// 故分类讨论if (y.n > x.n){size_t n(x.n);unsigned short *a(new unsigned short[y.n + 1]), *t(a + y.n); // 定义临时数组// 向临时数组输入数据*a = *p + *q;while (--n)*++a = *++p + *++q;while (++a < t)*a = *++q;// 处理首位if (*(a -= (n = y.n)--) > 255){*(a + 1) += *a / 256;*a %= 256;}// 处理头尾之间while (--n)if (*++a > 255){*(a + 1) += *a / 256;*a %= 256;}// 处理最高位if (*++a > 255) // 超过了unsigned char表示的范围{// 拷贝、释放并返回unsigned char *u(new unsigned char[y.n + 1] + (n = y.n));*u = *a / 256;*a %= 256;do*--u = *--t;while (--n);delete[] t;return bigu(u, y.n + 1);}// 未超过unsigned char表示的范围unsigned char *u(new unsigned char[n = y.n] + y.n);*--u = *a;while (--n)*--u = *--a;delete[] a;return bigu(u, y.n);}// 此时x的位数大于等于y的位数// 操作过程同理if (x.n == 1)return bigu(*p + *q);size_t n(y.n);unsigned short *a(new unsigned short[x.n + 1]), *t(a + x.n);*a = *p + *q;while (--n)*++a = *++p + *++q;while (++a < t)*a = *++p;if (*(a -= (n = x.n)--) > 255){*(a + 1) += *a / 256;*a %= 256;}while (--n)if (*++a > 255){*(a + 1) += *a / 256;*a %= 256;}if (*++a > 255){unsigned char *u(new unsigned char[x.n + 1] + (n = x.n));*u = *a / 256;*a %= 256;do*--u = *--t;while (--n);delete[] t;return bigu(u, x.n + 1);}unsigned char *u(new unsigned char[n = x.n] + x.n);*--u = *a;while (--n)*--u = *--a;delete[] a;return bigu(u, x.n);
}
// 重载减法运算符
// 默认x>=y
bigu operator-(const bigu &x, const bigu &y)
{if (x.n == 1) // 由于y<=x,只需要1字节内存空间{unsigned char *a(new unsigned char[1]);*a = *x.p - *y.p;return bigu(a, 1);}const unsigned char *p(x.p), *q(y.p); // 读取short *a(new short[x.n]), *t(a), *r(a + x.n - 1); // 定义临时数组*t = *p - *q;size_t n(y.n);while (--n)*++t = short(*++p) - short(*++q); // 由于p,q为无符号指针,需要进行强制类型转换while (++t <= r)*t = *++p;if (*(t = a) < 0){--*(t + 1);*t += 256;} // 对首位进行退位操作while (++t < r)if (*t < 0){*(t + 1) += *t / 256 - 1;(*t %= 256) += 256;} // 对其余各位进行退位操作++t;while (!*--t); // 找到首个大于0的高位// 拷贝数据进新的合适大小的数组unsigned char *u(new unsigned char[n = ++t - a] + n);do*--u = *--t;while (t > a);delete[] a; // 防止内存泄漏return bigu(u, n);
}
// 重载右移运算符,用以输入
istream &operator>>(istream &c, bigu &x)
{delete[] x.p; // 释放原来的数组,防止内存泄漏char s; // 由于不知道大数的上限,采用逐字节读取方式doc.get(s);while (s == ' ' || s == '\n' || s == '\t'); // 排除空格、水平制表符和换行if (s < '0' || s > '9') // 如果输入的不是数字,按0处理{*(x.p = new unsigned char[x.n = 1]) = 0;return c;}unsigned short *a(new unsigned short[x.n = 10]), *b(a), *p; // 定义临时数组,用范围比unsigned char稍大的类型进行临时存储,a指向最低位,b指向目前的最高位*a = s - '0'; // 存储第一位c.get(s);while (s >= '0' && s <= '9'){(*(p = a) *= 10) += s - '0'; // 加上新数并乘10(位权)while (++p <= b)*p *= 10; // 每一位乘10if (*(p = a) > 255) // 最低位超过了unsigned char的范围if (p == b) // 只有1位的情况{*++b = *p / 256; // 改变尾指针*p %= 256;goto F; // 直接进行下一位的读取}else{*(p + 1) += *p / 256;*p %= 256;}else if (p == b)goto F; // 只有1位而且不需要处理while (++p < b) // 处理头尾之间的数据if (*p > 255){*(p + 1) += *p / 256;*p %= 256;}if (*p > 255) // 最高位需要进位if (++b - a == x.n) // 事先的上限不够用,重新申请更大的存储空间并拷贝{unsigned short *u(a), *v(new unsigned short[x.n += 10]);*(p = v) = *a;while (++a < b)*++p = *a;delete[] u;a = v;*(b = p + 1) = *p / 256;*p %= 256;}else // 够用,直接处理{*b = *p / 256;*p %= 256;}F:c.get(s);}// 将处理结果拷贝进unsigned char数组并释放临时数组unsigned char *q(x.p = new unsigned char[x.n = ++b - (p = a)]);*q = *p;while (++p < b)*++q = *p;delete[] a;return c;
}
// 重载左移运算符,用以输出
ostream &operator<<(ostream &c, const bigu &x)
{size_t m(10); // 先假定一个上限unsigned short *t(new unsigned short[m]), *a(t), *b(t); // 用范围比unsigned char稍大的类型进行临时存储,a指向最低位,b指向目前的最高位const unsigned char *q(x.p + x.n); // 用于读取数据的指针if ((*t = *--q) > 9)do{*++b = *t / 10;*t %= 10;} while (*++t > 9); // 如果最高位大于等于10while (--q >= x.p){(*(t = a) *= 256) += *q; // 新位加上原来的乘上位权(256)while (++t <= b)*t *= 256; // 各高位乘位权(256)t = a;while (t != b) // 处理头尾之间的位,使它们都小于10{*(t + 1) += *t / 10;*t %= 10;++t;}if (*t > 9) // 如果最高位需要处理{if (false)F:{ // 预先的上限不够用,重新申请更大的存储空间unsigned short *u(t = new unsigned short[m += 30]), *v(a);*u = *a;while (++a < b)*++u = *a;delete[] v;a = t;t = b = u;}// 处理最高位do{if (++b - a == m)goto F;*b = *t / 10;*t++ %= 10;} while (*b > 9);}}c << *b; // 输出最高位while (--b >= a)c << *b; // 输出其余各位delete[] a; // 释放临时数组return c;
}
// 主函数
// 用以测试数据
int main()
{bigu a, b;cout << "请输入大数a:\n";cin >> a;cout << "请输入大数b:\n";cin >> b;cout << "a+b=" << a + b << endl;if (a > b)cout << "a-b=" << a - b << endl;elsecout << "b-a=" << b - a << endl;system("pause");return 0;
}
四、测试分析
第一组
第二组
实验中出现的bug及解决方案
实验中出现的bug:忽略了大数的进位、退位问题。
解决方案:定义更大的临时数据数组并逐位处理。
五、设计的特点和结果
设计采用的数据结构为数组。为了表示大数,把unsigned char
数组看作256进制的数,而不是十进制数,节约了内存空间,并利用动态内存分配技术,解决了大数问题。在实验过程中,我原本是打算逐个扩充数组(每次数组长度加1),后来发现运行的效率很低,就采用了和标准库类型vector
类似的内存分配方法,每次多扩充29位,解决了效率低的问题。