1.前言
实现大数的加减乘除是今年寒假C++的大作业,原本我是用字符串写的,但是后来看作业要求要用链表实现,于是我不情不愿的用链表的实现了一遍
2.Num类
2.1Node类
首先是内部的一个Node类用于建立单向链表,size用于计算大小方便Num对象之间做比较
class Node
{
public:
int val;
Node*next;
Node(int v, Node*n)
{
val = v;
next = n;
}
};
Node*head;
int size;
2.2构造函数,赋值函数以及析构函数
首先是默认构造函数,其实可能不会使用到,但为了方便,还是写了
Num()
{
head = NULL;
size = 0;
D = false;
}
然后是主要的一个构造函数,当遇到‘.‘的时候,我们用-100来作为标记
该链表为逆向构造的一个数,方便进行加减操作
Num(const string&s)
{
D = false;
head = NULL;
size = 0;
for (int i = 0; i < s.size(); i++)
{
if (s[i] == ','||s[i]=='-')
continue;
size++;
if (s[i] == '.')
head = new Node(-100, head);
else
head = new Node(s[i] - '0', head);
}
}
接下来是拷贝构造函数以及赋值函数,两者基本是一样的
值得注意的是,赋值函数要首先确认赋值对象是否为本身,若为本身,则返回
Num(const Num& num)
{
D = num.D;
size = num.size;
head = NULL;
Node*cur = num.head, *temp=head;
while (cur)
{
if (!head)
{
head = new Node(cur->val, NULL);
temp = head;
}
else
{
temp->next = new Node(cur->val, NULL);
temp = temp->next;
}
cur = cur->next;
}
}
Num& operator=(const Num&num)
{
if (this == &num)
return *this;
free();
size = num.size;
head = NULL;
D = num.D;
Node*cur = num.head, *temp=head;
while (cur)
{
if (!head)
{
head = new Node(cur->val, NULL);
temp = head;
}
else
{
temp->next = new Node(cur->val, NULL);
temp = temp->next;
}
cur = cur->next;
}
return *this;
}
最后是析构函数,我们析构函数只需要回收掉链表的内存即可
为了方便函数内部回收链表内存,将回收操作写为free函数
~Num()
{
free();
}
void free()
{
size = 0;
while (head)
{
Node*temp = head->next;
delete head;
head = temp;
}
head = NULL;
}
上述即为Num类的基本组成函数
2.3加法操作与减法操作
为方便操作,加减时大数在前,小数在后,直接对本身对象进行操作,所以使用+=,-=以及返回对象本身引用
加减操作没什么好说的,从最低位开始依次相加
在进位或借位时进行了一个判断是否为小数点的操作,以防操作错误
Num& operator+=(const Num&num)
{
Node*pa = head, *pb = num.head;
while (pa||pb)
{
int b = 0;
if (pb)
{
b = pb->val;
pb = pb->next;
}
if (pa->val != -100)
{
pa->val += b;
if (pa->val > 9)
{
if (pa->next)
{
if (pa->next->val == -100)
pa->next->next->val++;
else
pa->next->val++;
}
else
{
pa->next = new Node(1, NULL);
size++;
}
pa->val -= 10;
}
}
pa = pa->next;
}
return *this;
}
Num& operator-=(const Num&num)
{
Node*pa = head, *pb = num.head;
while (pa || pb)
{
int b = 0;
if (pb)
{
b = pb->val;
pb = pb->next;
}
if (pa->val != -100)
{
pa->val -= b;
if (pa->val < 0)
{
if (pa->next->val == -100)
pa->next->next->val--;
else
pa->next->val--;
pa->val += 10;
}
}
pa = pa->next;
}
return *this;
}
2.4乘法操作
乘法操作其实和列式计算一样,涉及到进位,但很容易理解
Num operator*(const Num&num)
{
Num res(string(size + num.size, '0'));
Node *pr = res.head, *temp;
for (Node*pa = head; pa != NULL; pa = pa->next)
{
int carry = 0;
temp = pr;
for (Node*pb = num.head; pb != NULL; pb = pb->next,pr=pr->next)
{
int temp = pa->val*pb->val + carry + pr->val;
pr->val = temp % 10;
carry = temp / 10;
}
pr->val += carry;
pr = temp->next;
}
return res;
}
2.5除法操作
除法操作我们使用被除数减去除数乘以若个干10进行
比如58除以5 首先用58-50=8 然后商加10 然后用8-5=3 商加1 由于3比5小,就不进行操作了 最终结果为11
但是题目要求除法操作要保留十位小数四舍五入,我们只需要事先对被除数乘以10^11即可,最终再加上小数点进行四舍五入操作
我们首先实现了两个与10有关的函数
void Mul10(const int& x)
{
for (int i = 0; i < x; ++i)
{
head = new Node(0, head);
size++;
}
}
void Div10(const int&x)
{
for (int i = 0; i < x; ++i)
{
Node*temp = head->next;
delete head;
head = temp;
size--;
}
}
然后是除法操作,res用于返回结果,p用于与res相加
d用于与被除数相减
Num operator/(const Num&num)
{
Num res(string(1, '0')), p(string(1, '1'));
res.D = true;
p.D = true;
Num d = num;
while (num<=*this)
{
int len = size - num.size;
d.Mul10(len);
p.Mul10(len);
int z = 0;
if (*this < d)
{
d.Div10(1);
p.Div10(1);
z = -1;
}
if (res.head->val == 0&&res.size==1)
res = p;
else
res += p;
*this -= d;
balance();
d.Div10(len + z);
p.Div10(len + z);
}
return res;
}
我们注意到了除法操作中有比较<的操作,接下来便是重载<函数