高精度减法(C语言实现)

打印 上一主题 下一主题

主题 912|帖子 912|积分 2736

高精度减法(C语言实现)

介绍

众所周知,整数在C和C++中以int ,long,long  long三种不同大小的数据存储,数据大小最大可达2^64,但是在实际使用中,我们仍不可避免的会遇到爆long  long的超大数运算,这个时候,就需要我们使用高精度算法,来实现巨大数的运算。
高精度的本质是将数字以字符串的形式读入,然后将每一位分别存放入int数组中,通过模拟每一位的运算过程,来实现最终的运算效果。
书接上回,我们今天继续讲解高精度减法的C语言实现:
代码实现
  1. #include<stdio.h>
  2. const int N = 100001;
  3. int cmp(int a[], int b[], int len1, int len2)
  4. {//大小比较函数
  5.         if (len1 > len2)//先对比长度
  6.                 return 0;
  7.         else if (len1 < len2)//长度不一样直接返回结果
  8.                 return 1;
  9.         else//长度一致则依次比较每一位大小
  10.         {
  11.                 for (int i = len1 - 1; i >= 0; i--)
  12.                 {
  13.                         if (a[i] > b[i])
  14.                                 return 0;
  15.                         if (a[i] < b[i])
  16.                                 return 1;
  17.                 }
  18.         }
  19.         return 0;//如果完全一致则返回0,避免减法函数中调用导致无限递归
  20. }
  21. int minus(int a[], int b[], int c[], int len1, int len2)
  22. {//高精度减法函数
  23.         if (cmp(a, b, len1, len2))//减法函数只计算大减小,小减大则反过来,然后输出时加负号
  24.                 return minus(b, a, c, len2, len1);
  25.         int t = 0;//t标识是否借位
  26.         for (int i = 0; i < len1; i++)
  27.         {
  28.                 c[i] = (a[i] - b[i] + t + 10) % 10;//c[i]表示这一位运算结果
  29.                 if (a[i] - b[i] + t < 0) t = -1;//计算是否借位
  30.                 else t = 0;
  31.         }
  32.         int len3 = len1;
  33.         while (c[len3 - 1] == 0)//去除前导0,返回结果的位数
  34.         {
  35.                 if (len3 == 1) return len3;
  36.                 len3--;
  37.         }
  38.         return len3;
  39. }
  40. int main()
  41. {
  42.         char str1[N], str2[N];//----------------------------
  43.         int a[N] = { 0 }, b[N] = { 0 }, c[N] = { 0 };
  44.         char x;
  45.         int len1 = 0, len2 = 0;
  46.         do
  47.         {
  48.                 scanf("%c", &x);
  49.                 str1[len1++] = x;
  50.         } while (x != '\n');
  51.         do//                                数据读入部分不作赘述
  52.         {
  53.                 scanf("%c", &x);
  54.                 str2[len2++] = x;
  55.         } while (x != '\n');
  56.         len1--; len2--;
  57.         for (int i = len1 - 1; i >= 0; i--)
  58.                 a[i] = str1[len1 - i - 1] - '0';
  59.         for (int i = len2 - 1; i >= 0; i--)
  60.                 b[i] = str2[len2 - i - 1] - '0';//---------------
  61.         int len3 = minus(a, b, c, len1, len2);//执行高精度减法函数
  62.         if (cmp(a, b, len1, len2))//大小比较函数
  63.                 printf("-");//结果为负数则打个负号先
  64.         for (int i = len3 - 1; i >= 0; i--)
  65.                 printf("%d", c[i]);
  66.         return 0;
  67. }
复制代码
思路解析

鉴于在高精度加法一篇中我们已经讲解过了数据的读入,所以我们这一篇不再赘述,没看过上一篇的可以点击下方链接:
高精度加法(C语言实现) - 凉茶coltea
高精度减法思路和高精度加法基本一致,区别就是加法考虑进位,减法考虑退位,以及减法的结果的位数变动是极大的。
我们对每一位分别计算,得出结果,存入新数组c,同时用临时变量t来标识是否借位。
但小数减大数的结果是负数,在实际操作中十分不便,所以我们另外声明一个cmp函数来比较二者大小,如果被减数比较小,那我们就可以用减数减去被减数,输出结果前先输出一个负号,达到同样的效果。
数据的读入上,高精度加减乘除基本一模一样,所以我们直接跳到第一个关键部分,大小比较函数:
  1. int cmp(int a[], int b[], int len1, int len2)
  2. {//大小比较函数
  3.         if (len1 > len2)//先对比长度
  4.                 return 0;
  5.         else if (len1 < len2)//长度不一样直接返回结果
  6.                 return 1;
  7.         else//长度一致则依次比较每一位大小
  8.         {
  9.                 for (int i = len1 - 1; i >= 0; i--)
  10.                 {
  11.                         if (a[i] > b[i])
  12.                                 return 0;
  13.                         if (a[i] < b[i])
  14.                                 return 1;
  15.                 }
  16.         }
  17.         return 0;//如果完全一致则返回0,避免减法函数中调用导致无限递归
  18. }
复制代码
在数据的读入中,我们已经知道了两数的位数,那就可以通过比较位数来判断二者大小谁长谁大。
倘若二者长度一致,那就依次比较每一位的大小,也就是比较二者的字典序。
倘若二者完全一致,那我们返回0,原因后面说。
有了大小比较函数,我们就可以保证计算时是大数减去小数了,这样,我们就规避了负数的困扰,可以更轻松地实现高精度减法的函数:
  1. int minus(int a[], int b[], int c[], int len1, int len2)
  2. {//高精度减法函数
  3.         if (cmp(a, b, len1, len2))//减法函数只计算大减小,小减大则反过来,然后输出时加负号
  4.                 return minus(b, a, c, len2, len1);
  5.         int t = 0;//t标识是否借位
  6.         for (int i = 0; i < len1; i++)
  7.         {
  8.                 c[i] = (a[i] - b[i] + t + 10) % 10;//c[i]表示这一位运算结果
  9.                 if (a[i] - b[i] + t < 0) t = -1;//计算是否借位
  10.                 else t = 0;
  11.         }
  12.         int len3 = len1;
  13.         while (c[len3 - 1] == 0)//去除前导0,返回结果的位数
  14.         {
  15.                 if (len3 == 1) return len3;
  16.                 len3--;
  17.         }
  18.         return len3;
  19. }
复制代码
如你所见,第一步就是对二者大小的判断,如果被减数比减数小,我们直接改变入参的顺序来改变二者位置。
倘若二者完全一致时cmp返回1,那么再调换位置后,minus函数将继续调用cmp函数来判断二者大小,每次都会返回1,导致无限递归,这就是我们规定完全一致时返回0的原因。
其中我们用c = (a - b + t + 10) % 10;来计算结果的第i位,之所以要+10,是模拟结果为负时向前一位借10的过程,而如果(a - b + t)不为负数,那因为%10的存在,也不会产生影响。
下一行if (a - b + t < 0)也很好理解,若是(a - b + t)为负数,那就需要向前一位借位,那我们就标记t=-1,来影响下一位的结果计算即可。
最后我们需要去除前导0,首先因为运算数都是正整数,所以结果最大位数也就和被减数一样,所以我们从被减数的最高位数开始判断结果c,如果为0,那就把返回的长度len3减去1,而值得注意的是,若是结果只有1位了那就不能减了,因为这意味着结果为0。
那此时我们就已经完成了高精度减法的运算,将结果存入了数组c,但别忘了结果正负的判断:
  1.         if (cmp(a, b, len1, len2))//大小比较函数
  2.                 printf("-");//结果为负数则打个负号先
复制代码
如果被减数比减数小,我们需要提前把负号补上。
那就此,大功告成。
结尾

那么以上便是对高精度减法算法的介绍,本文由凉茶coltea撰写,思路来自AcWing,大佬yxc的课程。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

玛卡巴卡的卡巴卡玛

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表