高精度算法之加减乘除简介

一.引言

在说高精度加减乘除运算之前,我们先搞明白什么是高精度运算?

高精度算法,属于处理大数字的数学计算方法。在一般的科学计算中,会经常算到小数点后几百位或者更多,当然也可能是几千亿几百亿的大数字。一般这类数字我们统称为高精度数,高精度算法是用计算机对于超大数据的一种模拟加,减,乘,除,乘方,阶乘,开方等运算。对于非常庞大的数字无法在计算机中正常存储,于是,将这个数字拆开,拆成一位一位的,或者是四位四位的存储到一个数组中, 用一个数组去表示一个数字,这样这个数字就被称为是高精度数。高精度算法就是能处理高精度数各种运算的算法,但又因其特殊性,故从普通数的算法中分离,自成一家。

说白了就是有时候你可能遇到100位相乘的运算,但是这个结果显然超过int类型的范围,long long int类型同样远超,所以我们就需要其他的处理方法来实现,而高精度算法就此孕育而生。

二.高精度加法:(不考虑负数)

高精度加法的实现原理:

1.把一个大数一位一位的存储到一个int数组中,数组索引1代表着个位,索引2代表着十位,以此类推。例如:

数组序号12345678910
数字A8747643451
数字B0596455204

数字A对应着1543467478
同理B是4025546950
确保数组中的每个索引对应的值均为一位数

2.将两数相加:

数组序号1234567891011
数字A8747643451
数字B0596455204
8244109655

此例中结果没有到11位,若超过10位则将11位填上

3.输出结果:
从高位到低位依次输出。即逆序遍历一遍数组即可。

4.代码实现如下:

int add(int *a, int *b, int acnt, int bcnt) {//高精度加法,结果存储到a数组中,返回相加结果的位数
 int cntt = acnt > bcnt ? acnt : bcnt;//确定加法所到的最高位
 for (int i = 1; i <= cntt; i++) {
  if (a[i] + b[i] >= 10) {//满10进位
   a[i + 1]++;
   a[i] = (a[i] + b[i]) % 10;//当前位则相加然后对10取模
  }
  else a[i] = a[i] + b[i];//不满10故直接相加
 }
 if (a[cntt + 1] != 0)return cntt + 1;//返回结果的位数
 else return cntt;

不用担心一次循环之后会有某个位的数值为两位数,因为两个一位数相加结果最大值为18,再加上前一位的进位只达到19,故一次循环即可,不用再检查一遍

练习题请参考洛谷P1601

加上负数其实就是减法,减法如下。

三.高精度减法:

原理同加法类似,毕竟减法本来就是加上负数而已

注意两点:

1.相减后为负数:
其实这个也很好处理。如果最高位减下来要小于0,那肯定就为负数了。判断完成后只需要执行输出-(b-a)即可。因为-(b-a)=-b+a=a-b,而且b-a>0。

2.a或b为负数:
借助高精度加法即可。

4.代码实现如下:

int cmp(int *a, int *b, int acnt, int bcnt) {//比较a和b谁大
 if (acnt > bcnt)return 1;
 else if (acnt < bcnt)return -1;
 else {
  for (int i = acnt; i >= 1; i--) {//从最高位一直比到个位判断谁大
   if (a[i] < b[i])return -1;
   else if (a[i] > b[i])return 1;
   else {
    if (a[i] == b[i]) {
     if (i == 1)return 0;
     else continue;
    }
   }
  }
 }
}
int sub(int *a, int *b, int acnt, int bcnt,int aflag,int bflag) {//高精度减法,返回相减结果的位数
 if (aflag == -1 && bflag == -1)return add(a, b, acnt, bcnt);
 //两数均为负数,则相加在加负号,add函数为前面的高精度加法函数,add函数只进行正整数加法,输出时通过flag标志判断是否加负号
 else if (aflag != -1 && bflag == -1)return add(a, b, acnt, bcnt);
 //减一个负数结果是加上其绝对值
 else if (aflag == -1 && bflag != -1)return sub(b, a, bcnt, acnt, bflag, -aflag);
 //负数减正数则对调两数位置即变成b-a,记得把aflag变相反数防止无限递归
 else if (cmp(a, b, acnt, bcnt) == 0)return 0;//两数相等,则值为0,表示结果没位数即为0
 else if (cmp(a, b, acnt, bcnt) < 0) {//a<b,故调换两数位置
   return sub(b, a, bcnt, acnt, bflag, aflag);
 }
 for (int i = 1; i <= acnt; i++) {//依次从个位开始相减
  if (a[i] < b[i]) {//当前位减不了则借位再减
   a[i + 1]--;
   a[i] += 10;
   a[i] = a[i] - b[i];
  }
  else {//当前位能减则直接减
   a[i] = a[i] - b[i];
  }
 }
 for (int i = acnt;; i--) {
  if (a[i] != 0)return i;//返回结果的位数
 }
}

练习题请参考洛谷P2142

四.高精度乘法:

高精度乘法原理:

由于我们是一位一位存储的,故适合的方法就是我们平时所列的式子算法,如下:

数A25
数B38
数A的个位乘数B190
数A的十位乘数B760
相加为积950

通过乘法分配率和结合律即可得25 * 38=5 * 38 + 20 *38。

代码实现如下:
代码只实现正数相乘,若有负数只需设置标志位,输出结果判断加入负号即可

int mul(int *a, int *b, int *mode, int acnt, int bcnt) {//高精度乘法
 int t, cntt = acnt > bcnt ? acnt : bcnt;
 for (int i = 1; i <= bcnt; i++) {//按照前面的原理依次展开乘法并相加的最后结果
  for (int j = 1; j <= acnt; j++) {
   if (i == 1) {//单独分开个位乘另一个数
    if (mode[j] + a[j] * b[i] >= 10) {//mode数组已初始化,超过十则进位
     t = (mode[j] + (a[j] * b[i])) / 10;
     mode[j] = (mode[j] + (a[j] * b[i])) % 10;
     mode[j + 1] += t;
    }
    else {
     mode[j] += a[j] * b[i];
    }
   }
   else {
    mode[j + i - 1] += a[j] * b[i];
    if (mode[j + i - 1] >= 10) {
     t = mode[j + i - 1] / 10;
     mode[j + i - 1] = mode[j + i - 1] % 10;
     mode[j + i] += t;
    }
   }
  }
 }
 for (int i = 1; i <= cntt * 2; i++) {//确保每个位上的数都是一位数
  if (mode[i] >= 10) {
   t = mode[i] / 10;
   mode[i] = mode[i] % 10;
   mode[i + 1] += t;
  }
 }
 for (int i = 2 * cntt;; i--) {返回结果的位数
  if (mode[i] != 0)return i;
 }
}

因为m位数乘n位数的结果会小于2 * max(m,n),故最后计算位数的时候从2 * cntt开始

练习题请参考洛谷P1303

五.高精度除法:

高精度除法实现原理:

高精度除法这一块比较复杂,它可以分为两种情况:第一种情况:高精除以低精,实际上就是对被除的每一位,包括前面的余数都除以除数即利用0~9次的循环减法模拟除法的过程,并计数,从而得到商的值。如图:


此代码仅讨论正整数的相除

代码实现如下:

int div(int *a,int *c, int acnt) {//此处的A[1]代表数的最高位
 int x = 0,b;
 cin >> b;
 for (int i = 1; i <= acnt; i++) {//按位相除
  c[i] = (x * 10 + a[i]) / b;
  x = (x * 10 + a[i]) % b;
 }
 int ccnt=1;
 while (c[ccnt] == 0 && ccnt < acnt)ccnt++;
 for (int i = ccnt; i <= acnt; i++)cout << c[i];
 cout << endl;
 return 0;
}

第二种情况:
高精度除以高精度,由于除法是乘法的逆过程,也就是说除法其实是求被除数有多少个除数相加,也就是说当被除数和除数都很大时,可以用减法代替除法达到计算的效果,但这不适合高精度除以低精度,因为容易超时。

代码实现如下:

#include<iostream>
using namespace std;
int a[100], b[100], c[100];
int cmp(int a[], int b[]){//比较a、b,若a>b为1;若a<b为-1;若a=b为0
 int i;
 if (a[0] > b[0])
  return 1;
 if (a[0] < b[0])
  return -1;
 for (i = a[0]; i > 0; i--){//从高位到低位比较
  if (a[i] > b[i])
   return 1;
  if (a[i] < b[i])
   return -1;
 }
 return 0;
}
void sub(int a[], int b[]){//计算a-b
 int flag;
 flag = compare(a, b);
 if (flag == 0){//相等
  a[0] = 0;
  return;
 }
 if (flag == 1){//大于
  for (int i = 1; i <= a[0]; i++){
   if (a[i] < b[i]){//若不够向上借位
    a[i + 1]--;
    a[i] += 10;
   }
   a[i] -= b[i];
  }
  while (a[0] > 0 && a[a[0]] == 0)//删除前导0
   a[0]--;
  return;
 }
}
int main(){
 char str1[100], str2[100];
 memset(a, 0, sizeof(a));
 memset(b, 0, sizeof(b));
 memset(c, 0, sizeof(c));
 cin >> str1 >> str2;
 a[0] = strlen(str1);//a[0]存储串1的位数
 b[0] = strlen(str2);//b[0]存储串2的位数
 for (int i = 1; i <= a[0]; i++)
  a[i] = str1[a[0] - i] - '0';
 for (int i = 1; i <= b[0]; i++)
  b[i] = str2[b[0] - i] - '0';
 int temp[100];
 c[0] = a[0] - b[0] + 1;//m位数除以n位数结果是m-n位数,初始化结果的位数
 for (int i = c[0]; i > 0; i--){
  memset(temp, 0, sizeof(temp));
  for (int j = 1; j <= b[0]; j++)//从i开始的地方,复制数组b到数组temp
   temp[j + i - 1] = b[j];
  temp[0] = b[0] + i - 1;
  while (cmp(a, temp) >= 0){//用减法模拟
   c[i]++;//成功减去一次则结果的当前位加一
   sub(a, temp);
  }
 }
 return 0;
}

由于整数除法会将小数点后面的全部截断,故在算才c[i]即结果的第i位时只需要知道b[i]到b[len]之间的数即可,至于b[i]到b[1]之间的数最后除出来的结果在小数位即可忽略。

事实上完全可以用高精度减法加上一个for循环无脑相减直到无法再减为止,只需记得每减一次答案加一并更新被减数的值即可。

至于除法的余数,很显然最后没减成功的一次所保留在a[ ]中的便是此次运算的余数。

总结:

通过以上加减乘除运算我们发现,高精度算法其实或多或少都是基于加减法的,同时负数加法又基于减法,减法又基于加法,乘法依靠加法,而除法则是依赖减法,至于没有讨论到的取模运算仍是依赖于除法和减法。而且你会发现事实上高精度乘法和除法的原理居然就是我们平时做乘法和除法的原理,只是我们平时被复杂的算式所蒙蔽,没看到其中的本质。

会发现我们以上讨论的都是10进制数的运算,那么作为收尾,我们来一道其他进制
题目快落快落。

洛谷P1604 B进制星球

题目背景 进制题目,而且还是个计算器~~ 题目描述
话说有一天,小Z乘坐宇宙飞船,飞到一个美丽的星球。因为历史的原因,科技在这个美丽的星球上并不很发达,星球上人们普遍采用B(2<=B<=36)进制计数。星球上的人们用美味的食物招待了小Z,作为回报,小Z希望送一个能够完成B进制加法的计算器给他们。
现在小Z希望你可以帮助他,编写实现B进制加法的程序。 输入输出格式 输入格式:

共3行第1行:一个十进制的整数,表示进制B。第2-3行:每行一个B进制数正整数。数字的每一位属于{0,1,2,3,4,5,6,7,8,9,A,B……},每个数字长度<=2000位。
输出格式:

一个B进制数,表示输入的两个数的和。 输入输出样例

输入样例#1: 复制 4 123 321

输出样例#1: 复制 1110

说明 进制计算器
题目链接

我们能发现这题其实和10进制加法区别不大,只是变成了逢B进位而不是逢10进位,储存方式和高精度加法一样,只是现在每个数组空间不仅仅只存储一位数,而是引入两位数,只是这个两位数需要保证小于B。

代码实现如下:

#include<iostream>
using namespace std;
int a[2010], b[2010], c[2010],y, x, ncnt, mcnt, z;
char n[2001], m[2001];
int main() {
 cin >> z;
 cin >> n; cin >> m;
 ncnt = strlen(n); mcnt = strlen(m);
 for (int i = 0; i < ncnt; i++)
  if (z > 10 && n[i] >= 'A') a[ncnt - i] = n[i] - 'A' + 10;//字符串逆序存储
  else a[ncnt - i] = n[i] - '0';//遇到字母转换成数字
 for (int i = 0; i < mcnt; i++)
  if (z > 10 && m[i] >= 'A') b[mcnt - i] = m[i] - 'A' + 10;
  else b[mcnt - i] = m[i] - '0';
 while (x <= ncnt || x <= mcnt) {//高精度加法
  x++;//x是位数指针
  c[x] = y + a[x] + b[x]; //y是进位
  y = c[x] / z; 
  c[x] %= z;//逢z进1
 }
 while (c[x] == 0 && x > 1) x--; //去前导零
 for (int i = x; i >= 1; i--) {
  if (c[i] < 10) cout << c[i];
  else cout << (char)(c[i] + 'A' - 10);
 }
 return 0;
}
  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 编译器可以算的最大位数取决于使用的数据类型和硬件平台的限制。一般来说,编译器可以处理标准数据类型,如整数、浮点数等。整数的位数可以根据不同的数据类型而有所不同,例如,32位的整数可以表示介于-2^31到2^31-1之间的整数,而64位的整数可以表示介于-2^63到2^63-1之间的整数。浮点数的精度也取决于其数据类型,例如,float类型通常具有32位精度,double类型通常具有64位精度。对于更大的数字,需要使用高精度算法或者大数库来处理。 ### 回答2: 编译器可以根据不同的位数进行加减乘除运算。在计算机中,位数通常是指二进制系统中数字的位数。常见的位数有32位和64位。编译器可以处理不同位数的运算,但具体能处理的最大位数取决于所使用的编译器和硬件平台的限制。 例如,32位编译器可以处理32位二进制数的加减乘除运算。这意味着它可以处理范围在-2^31到2^31-1之间的整数运算。超出此范围的数值可能导致溢出错误或意想不到的结果。 而64位编译器可以处理64位二进制数的加减乘除运算。这意味着它可以处理更大的整数范围,在-2^63到2^63-1之间进行运算。64位系统的运算精度更高,可以处理更大的整数或更精确的小数运算。 需要注意的是,浮点数运算的位数不同于整数运算。浮点数运算使用的是IEEE 754标准,一般有32位和64位两种精度。这意味着32位编译器可以处理32位浮点数的加减乘除运算,而64位编译器可以处理64位浮点数的加减乘除运算。 总的来说,编译器的运算位数取决于所使用的编译器和硬件平台的限制,一般可以处理32位或64位的加减乘除运算。 ### 回答3: 编译器可以计算的加减乘除的最大位数取决于编译器的设计和实现。在绝大多数现代编译器中,基本数据类型(比如整数和浮点数)的长度通常是固定的,大小取决于编译器和目标平台的架构。 对于整数类型,通常编译器支持不同位数的整数,如8位、16位、32位或64位等。这意味着编译器可以计算这些位数范围内的整数的加减乘除运算。 例如,对于32位整数,编译器可以对32位范围内的整数执行加减乘除运算,这意味着编译器可以处理从-2,147,483,648到2,147,483,647之间的整数。 对于浮点数类型,通常有不同的精度级别,如单精度浮点数(32位)和双精度浮点数(64位)。编译器可以使用这些精度级别来执行浮点数的加减乘除运算。 总而言之,编译器可以处理的加减乘除的最大位数取决于编译器和目标平台的架构。不同的编译器和平台可能支持不同的位数。通常情况下,编译器可以处理32位或64位范围内的整数和浮点数的加减乘除运算。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值