【君义精讲】高精度计算

一、概念

1. 高精度计算

高精度计算是指参与运算的数的范围大大超出了标准数据类型能表示的范围的运算。
如100位数字和100位数字的加减乘除运算。
为处理高精度计算,我们使用数字数组来表示高精度数字。

2. 数字数组

数字数组:第0位置保存数字位数,而后从低位到高位保存各位数字,每个数组元素保存一位数字。

下标 0 1 2 3 4 5
含义 位数 个位 十位 百位 千位 万位
例:数字12345用数字数组表示为
下标 0 1 2 3 4 5
数值 5 5 4 3 2 1

数字数组必须初始化为0,因为进行高精度计算时,会默认数字数组各位都为0。
(以下N为常量,表示高精度数字最大位数)
将数组元素初始化为0的几种写法

  1. 设为全局变量: int a[N];
  2. 设为局部变量: int a[N] = {};
  3. 需要时,将数组各元素设为0: memset(a, 0, sizeof(a));

高精度计算使用数字数组模拟各种计算过程,完成各种计算。

3. 代码风格

具体写法上,有三种风格可以选择

  • 数组与函数
  • 结构体与函数
  • 类中重载运算符
    本文先给出数组与函数式写法,最后给出类中重载运算符的写法。
4. 解题过程

本人推荐,面对高精度问题时,可以先将各个量当成低精度数字,写出代码。然后再将代码转为高精度计算。以下介绍中,会给出每个高精度运算中的操作在如果是在低精度运算中的等价代码。

二、相关操作

1. 字符串转为数字数组

运算数读入时都是字符串,需要将字符串转为数字数组。
注意:字符串从左到右是从高位到低位的。如果从低位到高位填充数字数组,则必须从右向左遍历字符串。
该操作是输入时的必需操作,等价低精代码:cin >> a;

  • 字符数组转为数字数组
void toNum(char s[], int a[])
{
    a[0] = strlen(s);
    for(int i = 1; i <= a[0]; ++i)
        a[i] = s[a[0] - i] - '0';
}
  • string类对象转为数字数组
void toNum(string s, int a[])
{
    a[0] = s.length();
    for(int i = 1; i <= a[0]; ++i)
        a[i] = s[a[0] - i] - '0';
}
2. 输出数字

注意:要从高位遍历到低位
等价低精代码:cout << a;

void showNum(int a[])
{
    for(int i = a[0]; i >= 1; --i)
        cout << a[i];
}
3. 数字拷贝(赋值)

注意:从下标0开始赋值
等价低精代码:a = b;

void numcpy(int a[], int b[])
{
    for(int i = 0; i <= b[0]; ++i)
        a[i] = b[i];
}
4. 数字比较

先比较位数,如果位数相同,从高位到低位遍历比较
模仿strcmp函数,如果a比b大,返回1,如果a比b小,返回-1,如果二者相等,返回0

//比较两个数字数组 如果a比b大,返回1,如果a比b小,返回-1,如果二者相等,返回0
int numcmp(int a[], int b[])
{
    if(a[0] > b[0])//如果a的位数比较多
        return 1;
    else if (a[0] < b[0])//如果b的位数比较多
        return -1;
    else
    {
        for(int i = a[0]; i >= 1; --i)
        {
            if(a[i] > b[i])
                return 1;
            else if(a[i] < b[i])
                return -1;
        }
        return 0;
    }
}
5. 确定高精度数字的位数

该操作不是一个独立的运算,却是以下每种高精度运算中的基本操作。

  • 写法1:
    作用为:已知某数字一定大于等于该高精度数字的位数,而后确定该高精度数字的位数。
    方法为:i不断左移,直到i指向的数字不为0,i就是该数字的位数。
//已知i一定大于等于数字a的位数,该函数确定数字a的位数,并赋值给a[0]
void setLen(int a[], int i)
{
    while(a[i] == 0 && i > 1)
        i--;
    a[0] = i;
}
  • 写法2:
    已知从第i位到该数字的最高位都不为0,确定该数字的位数。
//已知从第i位到该数字的最高位都不为0,确定该数字的位数。
void setLen2(int a[], int i)
{
	while(a[i+1] != 0)
		i++;
	a[0] = i;
}

本文以下对setLen()函数的调用都采用写法1

三、高精度运算

高精度运算有两类:

  • 高精与低精的运算
  • 高精与高精的运算

尽管将低精数字转化为高精数字后,再进行高精与高精的运算,当然也可以完成运算。不过通常情况下,高精与低精数字间运算的复杂度要低于高精与高精数字间运算的复杂度。所以学习高精对低精运算是必要的。
注意:以下用于存储高精度运算结果的数字数组r必须在函数运行前初始化为0
常见操作:

  • 设表示进位的变量c
  • 更新进位:c = r[i] / 10
  • 更新这一位:r[i] %= 10

注意:高精对低精运算时,进位数字未必是0~9范围内的数字,可能是很大的数字。

1. 加法

1.1 高精加低精

将低精数字加到高精数字的个位,而后不断进位

  • 等价低精代码:r = a + b;
void Add(int a[], int b, int r[])//r初始化为0
{
    numcpy(r, a);
    int c = b, i = 1;
    while(c > 0)
    {
        r[i] += c;
        c = r[i] / 10;
        r[i] %= 10;
        i++;
    }
    if(i > r[0])
	    setLen(r, i);
}
  • 等价低精代码:a += b;
void Add(int a[], int b])
{
    int c = b, i = 1;
    while(c > 0)
    {
        a[i] += c;
        c = a[i] / 10;
        a[i] %= 10;
        i++;
    }
    if(i > a[0])
	    setLen(a, i);
}
1.2 高精加高精

模拟加法竖式,注意i要声明在循环外面。

  • 等价低精代码:r = a + b;
void Add(int a[], int b[], int r[])//r初始化为0
{
    int c = 0, i;
    for(i = 1; i <= a[0] || i <= b[0]; ++i)
    {
        r[i] = a[i] + b[i] + c;
        c = r[i] / 10;
        r[i] %= 10;
    }
    r[i] = c;
    setLen(r, i);
}
  • 等价低精代码:a += b;
void Add(int a[], int b[])
{
    int c = 0, i;
    for(i = 1; i <= a[0] || i <= b[0]; ++i)
    {
        a[i] += b[i] + c;
        c = a[i] / 10;
        a[i] %= 10;
    }
    a[i] = c;
    setLen(a, i);
}

2. 减法

如果不确定被减数和减数哪个更大,就用numcmp()函数先比较一下两个数,如果被减数更小,则先输出负号,再进行更大数字减更小数字的减法运算。
这里预先确定数字a大于等于数字b。

2.1 高精减低精

高精度数字的最低位减低精度数字,而后不断借位,直到每一位都大于等于0。

  • 等价低精代码:r = a - b;
void Minus(int a[], int b, int r[])//数a大于等于数b
{
    int i = 1, c;//c:借位数 
    numcpy(r, a);
    r[1] -= b;
    while(r[i] < 0)
    {
        if(-r[i] % 10 == 0)
            c = -r[i] / 10;
        else
            c = -r[i] / 10 + 1;
        r[i] += 10*c;
        r[i+1] -= c;
        i++;
    }
    setLen(r, r[0]);
}
  • 等价低精代码:a -= b
void Minus(int a[], int b)//数a大于等于数b
{
    int i = 1, c;//c:借位数 
    a[1] -= b;
    while(a[i] < 0)
    {
        if(-a[i] % 10 == 0)
            c = -a[i] / 10;
        else
            c = -a[i] / 10 + 1;
        a[i] += 10*c;
        a[i+1] -= c;
        i++;
    }
    setLen(a, a[0]);
}
2.2 高精减高精

模拟减法竖式

  • 等价低精代码:r = a - b;
void Minus(int a[], int b[], int r[])//数a大于等于数b
{
    int i, c = 0;
    for(i = 1; i <= a[0]; ++i)
    {
        r[i] = a[i] - c - b[i];
        c = 0;
        if(r[i] < 0)
        {
            c = 1;
            r[i] += 10;
        }
    }
    setLen(r, i);
}
  • 等价低精代码:a -= b;
void Minus(int a[], int b[])
{
    int i;
    for(i = 1; i <= a[0]; ++i)
    {
        a[i] -= b[i];
        if(a[i] < 0)
        {
            a[i+1] -= 1;
            a[i] += 10;
        }
    }
    setLen(a, i);
}

3. 乘法

2.1 高精乘低精(常用)

高精度数字的每一位乘低精度数字,而后不断进位。

  • 等价低精代码:r = a * b;
void Multiply(int a[], int b, int r[])
{
    int c = 0, i;
    for(i = 1; i <= a[0]; ++i)
    {
        r[i] = b * a[i] + c;
        c = r[i] / 10;
        r[i] %= 10;
    }
    while(c > 0)
    {
        r[i] = c % 10;
        c /= 10;
        i++;
    }
    setLen(r, i);
}
  • 等价低精代码:a *= b;
void Multiply(int a[], int b)
{
    int c = 0, i;
    for(i = 1; i <= a[0]; ++i)
    {
        a[i] = a[i]*b + c;
        c = a[i] / 10;
        a[i] %= 10; 
    }
    while(c > 0)
    {
        a[i] = c % 10;
        c /= 10;
        i++;
    }
    setLen(a, i);
}
2.2 高精乘高精

模拟乘法竖式

  • 等价低精代码:r = a * b;
void Multiply(int a[], int b[], int r[])
{
    for(int i = 1; i <= a[0]; ++i)
    {
        int c = 0;
        for(int j = 1; j <= b[0]; ++j)
        {
            r[i+j-1] += a[i]*b[j] + c;
            c = r[i+j-1] / 10;
            r[i+j-1] %= 10;
        }
        r[i+b[0]] += c;
    }
    setLen(r, a[0] + b[0]);
}
  • 等价低精代码:a *= b;
    没有直接实现该表达式的函数,可以间接的进行r = a * b, a = r;来实现a *= b;
void Multiply(int a[], int b[])
{
    int r[N] = {};
    Multiply(a, b, r);//调用上面有3个参数的Multiply函数
    numcpy(a, r);
}

4. 除法

4.1 高精除以低精

注意:从高位到低位除,模拟竖式

  • 等价低精代码:r = a / b;
void Divide(int a[], int b, int r[])
{
    int x = 0;//余数
    for(int i = a[0]; i >= 1; --i)
    {
        r[i] = (x * 10 + a[i]) / b;
        x = (x * 10 + a[i]) % b;
    }
    setLen(r, a[0]);
}
  • 等价低精代码:r = a % b;
int Mod(int a[], int b)//返回a % b的值
{
    int x = 0;
    for(int i = a[0]; i >= 1; --i)
        x = (x * 10 + a[i]) % b;
    return x;
}
4.2 高精除以高精
//a = a*10+num,即在数字数组的个位添加一位数,0 <= num <= 9,如2添加1后变为21
void addNum(int a[], int num) 
{
    if(a[0] == 1 && a[1] == 0)//如果a是0,那么就把个位设为num 
        a[1] = num;
    else
    {
        for(int i = a[0]; i >= 1; --i)//数组移位
            a[i+1] = a[i];
        a[1] = num;
        a[0]++;
    }
}
//高精度除法中的减法代替除法操作,实际就是除法的另一种实现方式
//已知商一定是0~9的数字。a是被除数 b是除数 返回值为商,计算后数组a保存的是余数
int divideByMinus(int a[], int b[])
{
    int q = 0, r[N];//q:商(减的次数) r:临时结果 
    while(numcmp(a, b) >= 0)
    {
        memset(r, 0, sizeof(r));
        Minus(a, b, r);
        numcpy(a, r);//将a设为上次减法的结果
        q++;
    }
    return q;
}
//r = a / b 高精度数字除以高精度数字
void Divide(int a[], int b[], int r[])
{
    int x[N] = {};//x:中间临时使用的数字,是上一次不断减数后余下的数,以及作为下一次的被减数
    for(int i = a[0]; i >= 1; --i)
    {
        addNum(x, a[i]);
        r[i] = divideByMinus(x, b);
    }
    setLen(r, a[0]); 
}
//x = a % b 高精度数字对高精度数字取模
void Mod(int a[], int b[], int x[])
{
    for(int i = a[0]; i >= 1; --i)
    {
        addNum(x, a[i]);
        divideByMinus(x, b);
    }
}

四、其他

1. 处理高精度k进制数

默认k是低精度数字。将k设为全局变量。
数字数组中每个元素保存k进制数中的一位,也就是一个0~k-1的数字
例:十六进制数ABCDE用数字数组表示为

下标 0 1 2 3 4 5
数值 5 14 13 12 11 10

在运算时,将原函数中所有出现的10替换为k。
例:高精乘低精

int k = 16;//进制数
void Multiply(int a[], int b)//a *= b
{
    int c = 0, i;
    for(i = 1; i <= a[0]; ++i)
    {
        a[i] = a[i]*b + c;
        c = a[i] / k;
        a[i] %= k; 
    }
    while(c > 0)
    {
        a[i] = c % k;
        c /= k;
        i++;
    }
    setLen(a, i);
}

在输出时,将数值转为n进制数码。
例:输出16进制数字

void showNum(int a[])
{
    for(int i = a[0]; i >= 1; --i)
        cout << (a[i] < 10 ? a[i] + '0' : a[i] - 10 + 'A');
}
2. 结构体与函数风格写法

在一个结构体中保存数字数组与数字位数
这样写的优点在于,可以将运算结果作为函数的返回值返回。而不是像上面函数中写的那样,需要用数组参数将结果带出来。

例:高精乘低精:

struct HPN
{
	int num[N], len;//num:数字数组 len:数字位数
};
HPN Multiply(HPN a, int b)
{
	HPN r;
	memset(r.num, 0, sizeof(r.num));
    int c = 0, i;
    for(i = 1; i <= a.len; ++i)
    {
        r.num[i] = b * a.num[i] + c;
        c = r.num[i] / 10;
        r.num[i] %= 10;
    }
    while(c > 0)
    {
        r.num[i] = c % 10;
        c /= 10;
        i++;
    }
    while(r.num[i] == 0 && i > 1)
    	i--;
    r.len = i;
    return r;
}

尽管写法稍有不同不同,算法的思想是相同的,其它运算函数本文不再赘述。
有一道用这种写法出的题,可以参考:
NOIP 2011 普及组初赛 第28题

3. 类中重载运算符风格写法

学过重载运算符知识后,可以尝试使用这种写法。
将高精度数字的相关操作以及高精度运算都写在类中,重载各种运算符。
最终效果可以使在写代码时,只需要考虑具体问题,而不需要考虑高精度相关的问题。按我们熟悉的低精度运算的写法将代码写出,即可实现高精度运算。

附录

高精度计算 数组与函数风格写法
#include 
using namespace std;
#define N 10000
//数字数组:
//第0位置保存数字位数,第1位置保存个位,第2位置保存十位...从低位到高位保存数字
//注意:以下高精度计算的所有函数的表示结果的参数数组r,在调用函数前都必须清零(memset(r, 0, sizeof(r));),清零语句不可以在函数内调用。 
//高精运算高精、高精运算低精的函数名相同,但参数类型不同,形成函数重载。两个函数都可以使用。

//将字符数组转化为数字数组 数字数组从第1位置到第len位置,从低位到高位保存各位数字,第0位置保存数字位数
void toNum(char s[], int a[])
{
    a[0] = strlen(s);
    for(int i = 1; i <= a[0]; ++i)
        a[i] = s[a[0] - i] - '0';
}
//输出数字数组
void showNum(int a[])
{
    for(int i = a[0]; i >= 1; --i)
        cout << a[i];
}
//已知i一定大于等于数字a的位数,该函数确定数字a的位数,并赋值给a[0]
void setLen(int a[], int i)
{
    while(a[i] == 0 && i > 1)
        i--;
    a[0] = i;
}
//将数字数组b复制到数字数组a之中
void numcpy(int a[], int b[])
{
    for(int i = 0; i <= b[0]; ++i)
        a[i] = b[i];
}
//比较两个数字数组 如果a比b大,返回1,如果a比b小,返回-1,如果二者相等,返回0
int numcmp(int a[], int b[])
{
    if(a[0] > b[0])//如果a的位数比较多
        return 1;
    else if (a[0] < b[0])//如果b的位数比较多
        return -1;
    else
    {
        for(int i = a[0]; i >= 1; --i)
        {
            if(a[i] > b[i])
                return 1;
            else if(a[i] < b[i])
                return -1;
        }
        return 0;
    }
}
//高精加低精 r = a + b 
void Add(int a[], int b, int r[])
{
    numcpy(r, a);
    int c = b, i = 1;
    while(c > 0)
    {
        r[i] += c;
        c = r[i] / 10;
        r[i] %= 10;
        i++;
    }
    if(i > r[0])
	    setLen(r, i);
}
//高精加低精 a += b 
void Add(int a[], int b)
{
    int c = b, i = 1;
    while(c > 0)
    {
        a[i] += c;
        c = a[i] / 10;
        a[i] %= 10;
        i++;
    }
    if(i > a[0])
	    setLen(a, i);
}
//高精加高精 r = a + b 
void Add(int a[], int b[], int r[])
{
    int c = 0, i;
    for(i = 1; i <= a[0] || i <= b[0]; ++i)
    {
        r[i] = a[i] + b[i] + c;
        c = r[i] / 10;
        r[i] %= 10;
    }
    r[i] = c;
    setLen(r, i);
}
//高精加高精 a += b
void Add(int a[], int b[])
{
    int c = 0, i;
    for(i = 1; i <= a[0] || i <= b[0]; ++i)
    {
        a[i] += b[i] + c;
        c = a[i] / 10;
        a[i] %= 10;
    }
    a[i] = c;
    setLen(a, i);
}
//高精减低精 r = a - b 前提:a >= b 
void Minus(int a[], int b, int r[])//数a大于等于数b
{
    int i = 1, c;//c:借位数 
    numcpy(r, a);
    r[1] -= b;
    while(r[i] < 0)
    {
        if(-r[i] % 10 == 0)
            c = -r[i] / 10;
        else
            c = -r[i] / 10 + 1;
        r[i] += 10*c;
        r[i+1] -= c;
        i++;
    }
    setLen(r, r[0]);
}
//高精减低精 a -= b,前提:a >= b
void Minus(int a[], int b)
{
    int i = 1, c;//c:借位数 
    a[1] -= b;
    while(a[i] < 0)
    {
        if(-a[i] % 10 == 0)
            c = -a[i] / 10;
        else
            c = -a[i] / 10 + 1;
        a[i] += 10*c;
        a[i+1] -= c;
        i++;
    }
    setLen(a, a[0]);
}
//高精减高精 r = a - b 前提:a >= b 
void Minus(int a[], int b[], int r[])
{
    int i, c = 0;
    for(i = 1; i <= a[0]; ++i)
    {
        r[i] = a[i] - c - b[i];
        c = 0;
        if(r[i] < 0)
        {
            c = 1;
            r[i] += 10;
        }
    }
    setLen(r, i);
}
//高精减高精 a -= b,前提 a>=b 
void Minus(int a[], int b[])
{
    int i;
    for(i = 1; i <= a[0]; ++i)
    {
        a[i] -= b[i];
        if(a[i] < 0)
        {
            a[i+1] -= 1;
            a[i] += 10;
        }
    }
    setLen(a, i);
}
//高精乘低精 r = a * b
void Multiply(int a[], int b, int r[])
{
    int c = 0, i;
    for(i = 1; i <= a[0]; ++i)
    {
        r[i] = b * a[i] + c;
        c = r[i] / 10;
        r[i] %= 10;
    }
    while(c > 0)
    {
        r[i] = c % 10;
        c /= 10;
        i++;
    }
    setLen(r, i);
}
//高精乘低精 a *= b
void Multiply(int a[], int b)
{
    int c = 0, i;
    for(i = 1; i <= a[0]; ++i)
    {
        a[i] = a[i]*b + c;
        c = a[i] / 10;
        a[i] %= 10; 
    }
    while(c > 0)
    {
        a[i] = c % 10;
        c /= 10;
        i++;
    }
    setLen(a, i);
}
//高精乘高精 r = a * b 
void Multiply(int a[], int b[], int r[])
{
    for(int i = 1; i <= a[0]; ++i)
    {
        int c = 0;
        for(int j = 1; j <= b[0]; ++j)
        {
            r[i+j-1] += a[i]*b[j] + c;
            c = r[i+j-1] / 10;
            r[i+j-1] %= 10;
        }
        r[i+b[0]] += c;
    }
    setLen(r, a[0] + b[0]);
}
//高精乘高精 a *= b
void Multiply(int a[], int b[])
{
    int r[N] = {};
    Multiply(a, b, r);//调用上面有3个参数的Multiply函数
    numcpy(a, r);
} 
//高精除以低精 r = a / b
void Divide(int a[], int b, int r[])
{
    int x = 0;//余数
    for(int i = a[0]; i >= 1; --i)
    {
        r[i] = (x * 10 + a[i]) / b;
        x = (x * 10 + a[i]) % b;
    }
    setLen(r, a[0]);
}
//高精模低精 返回值 = a % b
int Mod(int a[], int b)//返回a % b的值
{
    int x = 0;
    for(int i = a[0]; i >= 1; --i)
        x = (x * 10 + a[i]) % b;
    return x;
}
//高精除以高精
 //a = a*10+num,即在数字数组的个位添加一位数,0 <= num <= 9,如2添加1后变为21
void addNum(int a[], int num) 
{
    if(a[0] == 1 && a[1] == 0)//如果a是0,那么就把个位设为num 
        a[1] = num;
    else
    {
        for(int i = a[0]; i >= 1; --i)//数组移位
            a[i+1] = a[i];
        a[1] = num;
        a[0]++;
    }
}
//高精度除法中的减法代替除法操作,实际就是除法的另一种实现方式
//已知商一定是0~9的数字。a是被除数 b是除数 返回值为商,计算后数组a保存的是余数
int divideByMinus(int a[], int b[])
{
    int q = 0, r[N];//q:商(减的次数) r:临时结果 
    while(numcmp(a, b) >= 0)
    {
        memset(r, 0, sizeof(r));
        Minus(a, b, r);
        numcpy(a, r);//将a设为上次减法的结果
        q++;
    }
    return q;
}
//r = a / b 高精度数字除以高精度数字
void Divide(int a[], int b[], int r[])
{
    int x[N] = {};//x:中间临时使用的数字,是上一次不断减数后余下的数,以及作为下一次的被减数
    for(int i = a[0]; i >= 1; --i)
    {
        addNum(x, a[i]);
        r[i] = divideByMinus(x, b);
    }
    setLen(r, a[0]); 
}
//x = a % b 高精度数字对高精度数字取模
void Mod(int a[], int b[], int x[])
{
    for(int i = a[0]; i >= 1; --i)
    {
        addNum(x, a[i]);
        divideByMinus(x, b);
    }
}
int main()
{//例子:测试高精乘高精 
    int a[N] = {}, b[N] = {}, r[N] = {};
    char s1[N], s2[N];
    cin>>s1>>s2;
    toNum(s1, a);
    toNum(s2, b);    
    Multiply(a, b, r);
    showNum(r);
    return 0;
}

高精度计算 类中重载运算符风格
#include 
using namespace std;
#define N 1005
struct HPN
{//使用默认拷贝构造函数、默认赋值符号 
    int a[N];//数字数组 a[0]表示数字位数 低位到高位存储 
    HPN()
    {
        memset(a, 0, sizeof(a));
    }
    HPN(char s[])//用字符数组构造高精度数字 
    {
        memset(a, 0, sizeof(a));
        int len = strlen(s);
        for(int i = 0; i < len; ++i)
            a[len - i] = s[i] - '0';
        a[0] = len;
    }
    int& operator [] (int i)//重载中括号,可以让高精度数字对象类似数组一样取值
    {
        return a[i];
    }
    friend ostream & operator << (ostream &out, HPN &b)//友元函数,使该高精度数字可以用cout输出 
    {
        for(int i = b[0]; i >= 1; --i)
            out << b[i];
        return out;
    }
    void show()//一般输出 
    {
        for(int i = a[0]; i >= 1; --i)
            cout << a[i];
        cout << endl;
    }
    int numcmp(int a[], int b[])
    {
        if(a[0] > b[0])//如果a的位数比较多
            return 1;
        else if (a[0] < b[0])//如果b的位数比较多
            return -1;
        else
        {
            for(int i = a[0]; i >= 1; --i)
            {
                if(a[i] > b[i])
                    return 1;
                else if(a[i] < b[i])
                    return -1;
            }
            return 0;
        }
    }
    bool operator > (HPN b)//判断自己比b大
    {
        return numcmp(a, b.a) > 0;
    }
    bool operator < (HPN b)//判断自己比b小 
    {
        return numcmp(a, b.a) < 0;
    }
    bool operator == (HPN b)//判断自己与b相等 
    {
        return numcmp(a, b.a) == 0;
    }
    bool operator >= (HPN b)//判断自己大于等于b 
    {
        return numcmp(a, b.a) >= 0;
    }
    bool operator <= (HPN b)//判断自己小于等于b 
    {
        return numcmp(a, b.a) <= 0;
    }
    bool operator != (HPN b)//判断自己不等于b 
    {
        return numcmp(a, b.a) != 0;
    }
    void setLen(int i)//从第i位置开始,向低位寻找,直到找到一个不为0的数位,更新数字长度
    {
        while(a[i] == 0 && i > 1)
            i--;
        a[0] = i;
    }
    HPN operator + (int b)//高精加低精
    {
        HPN r(*this);//this是指向自己这个对象的指针,*this就是自己这个对象。这里用了默认拷贝构造函数。 
        int c = b, i = 1;
        while(c > 0)
        {
            r[i] += c;
            c = r[i] / 10;
            r[i] %= 10;
            i++;
        }
        if(i > r[0])
            r.setLen(i);
        return r;
    }
    void operator += (int b)//高精加低精
    {
        int c = b, i = 1;
        while(c > 0)
        {
            a[i] += c;
            c = a[i] / 10;
            a[i] %= 10;
            i++;
        }
        if(i > a[0])
    	    setLen(i);
    }
    HPN operator + (HPN b)//高精加高精
    {
        HPN r;
        int i, c = 0;
        for(i = 1; i <= a[0] || i <= b[0]; ++i)
        {
            r[i] = a[i] + b[i] + c;
            c = r[i] / 10;
            r[i] %= 10;
        }
        r[i] = c;
        r.setLen(i);
        return r;
    }
    void operator += (HPN b)//高精加高精
    {
        int c = 0, i;
        for(i = 1; i <= a[0] || i <= b[0]; ++i)
        {
            a[i] += b[i] + c;
            c = a[i] / 10;
            a[i] %= 10;
        }
        a[i] = c;
        setLen(i);
    }
    HPN operator - (int b)//高精减低精 本身比b大 
    {
        HPN r(*this);//使用拷贝构造函数 
        int i = 1, c;//借位数 
        r[1] -= b;
        while(r[i] < 0)
        {
            if(-r[i] % 10 == 0)
                c = -r[i] / 10;
            else
                c = -r[i] / 10 + 1;
            r[i] += 10*c;
            r[i+1] -= c;
            i++;
        }
        r.setLen(r[0]);
        return r;
    }
    void operator -= (int b)//高精减低精
    {
        int i = 1, c;//c:借位数 
        a[1] -= b;
        while(a[i] < 0)
        {
            if(-a[i] % 10 == 0)
                c = -a[i] / 10;
            else
                c = -a[i] / 10 + 1;
            a[i] += 10*c;
            a[i+1] -= c;
            i++;
        }
        setLen(a[0]);
    }
    HPN operator - (HPN b)//高精减高精 前提本数字比b大
    {
        HPN r(*this);
        int i, c = 0;
        for(i = 1; i <= r[0] || i <= b[0]; ++i)
        {
            if(r[i] < b[i])
            {
                r[i + 1]--;
                r[i] += 10;
            }
            r[i] = r[i] - b[i];
        }
        r.setLen(i);
        return r;
    }
    void operator -= (HPN b)//高精减高精
    {
        int i;
        for(i = 1; i <= a[0]; ++i)
        {
            a[i] -= b[i];
            if(a[i] < 0)
            {
                a[i+1] -= 1;
                a[i] += 10;
            }
        }
        setLen(i);
    }
    HPN operator * (int b)//高精乘低精
    {
        HPN r;
        int c = 0, i;
        for(i = 1; i <= a[0]; ++i)
        {
            r[i] = b * a[i] + c;
            c = r[i] / 10;
            r[i] %= 10;
        }
        while(c > 0)
        {
            r[i] = c;
            c = r[i] / 10;
            r[i] %= 10;
            i++;
        }
        r.setLen(i);
        return r;
    }
    void operator *= (int b)//高精乘低精
    {
        int c = 0, i;
        for(i = 1; i <= a[0]; ++i)
        {
            a[i] = a[i]*b + c;
            c = a[i] / 10;
            a[i] %= 10; 
        }
        while(c > 0)
        {
            a[i] = c % 10;
            c /= 10;
            i++;
        }
        setLen(i);
    }
    HPN operator * (HPN b)//高精乘高精
    {
        HPN r;
        for(int i = 1; i <= a[0]; ++i)
        {
            int c = 0;
            for(int j = 1; j <= b[0]; ++j)
            {
                r[i + j - 1] = a[i] * b[j] + r[i + j - 1] + c;
                c = r[i + j - 1] / 10;
                r[i + j - 1] %= 10;
            }
            r[i + b[0]] += c;
        }
        r.setLen(a[0] + b[0]);
        return r;
    }
    void operator *= (HPN b)//高精乘高精
    {
        (*this) = (*this) * b;
    }
    HPN operator / (int b) //高精除低精
    {
        int x = 0;
        HPN r;
        for(int i = a[0]; i >= 1; --i)
        {
            x = x * 10 + a[i];
            r[i] = x / b;
            x %= b;
        }
        r.setLen(a[0]);
        return r;
    }
    void operator /= (int b) //高精除低精
    {
        int x = 0;
        for(int i = a[0]; i >= 1; --i)
        {
            x = x * 10 + a[i];
            a[i] = x / b;
            x %= b;
        }
        setLen(a[0]);
    }
    int operator % (int b) //高精模低精
    {
        int x = 0;
        for(int i = a[0]; i >= 1; --i)
            x = (x * 10 + a[i]) % b;
        return x;
    }
    void addNum(HPN &x, int num)//x = x*10 + num 
    {
        if(x[0] == 1 && x[1] == 0)//如果x是0,那么就把个位设为num 
            x[1] = num;
        else 
        {//以下几句完成x = x * 10 + b 
            for(int j = x[0]; j >= 1; --j)
                x[j + 1] = x[j];
            x[1] = num;
            x[0]++;
        }
    }
    HPN operator / (HPN b) //高精除高精
    {
        HPN r, x;
        for(int i = a[0]; i >= 1; --i)
        {
            addNum(x, a[i]); 
            //求r[i] = x / b; 以减法代替除法
            int q = 0;//记录减了几次,该值就是通过减法代替除法得到的x/b的商
            while(x >= b)//高精度数字比较
            {
                x = x - b;//高精度数字减法
                q++;
            }
            r[i] = q;
        }
        r.setLen(a[0]);
        return r;
    }
    HPN operator % (HPN b) //高精模高精
    {
        HPN x;
        for(int i = a[0]; i >= 1; --i)
        {
            addNum(x, a[i]);
            //求x = x % b; 以减法代替除法
            while(x >= b)//高精度数字比较 
                x = x - b;
        }
        return x;
    }
};

int main()
{
    char c[40], d[40];
    cin>>c>>d;
    HPN a(c), b(d);
    a /= 3;
    cout << a;
    return 0;
}

你可能感兴趣的:(相关知识,高精度)