高精度四则运算程序实现

12 篇文章 0 订阅

高精度对于有些语言来说都有成型的库函数供程序员使用,但是其中对于算法的考量来说,我们还是希望重新感受一下实现的过程。

以c++为例,对于一般的 int,long,或者 long long 类型的能够满足大多数场景,但对于成百上千位的大数来说这些就无能为力了。

本文参考相关资料整理了加减乘除的大数算法小题目。 回顾一下最基本的运算规则用程序如何实现。

大数加法

给定两个正整数,不含前导0, 计算他们的和。
整数长度范围 [1, 100000]

实现思路也比较直接,我们无非是要按位相加,时刻记住进位补数即可,根本在于我们用一个数组的结构来存储数的每一位。

#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;

int main() {
   
   string a, b; cin >> a >> b;
   reverse(a.begin(), a.end());
   reverse(b.begin(), b.end());
   //string res = "";
   vector<int> res;
   int t = 0;
   for (size_t i = 0, j = 0; i < a.size() ||  j < b.size(); i++, j++) {
       if (i < a.size()) t += a[i] - '0';
       if (j < b.size()) t += b[j] - '0';
       //res =  to_string(t % 10) + res;
       res.push_back(t % 10);
       t = t / 10;
   }
   if (t > 0) res.push_back(t);
   for (int i = res.size() - 1; i >= 0; i--) cout << res[i];
   //if(t > 0) res = to_string(t) + res;
   //cout << res;
   return 0;    
}

从注释中可以看出我原来用的是字符串拼接和存储中间结果,实际的效果比当前时间耗时高一个数量级,因此是不可取的,相信可以猜到字符串拼接存在大量的空间重分配和拷贝操作,具体原因可能到细究一下 c++ 的string 实现的考量,这个问题其实不简单,希望有机会能写写。

大数减法

给定两个正整数(不含前导 0),计算它们的差,计算结果可能为负数。

与加法不同的是,减法考虑的是从高位借位。尤其最后可能会出现前导0,要处理一下。

#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;


bool cmp1(string a, string b) {
    if (a.size() != b.size()) return a.size() < b.size();
    return a < b; 
}

vector<int> sub(string a, string b) {
   vector<int> res;
   int borrow = 0;
   for (int i = 0, j = 0; i < a.size() || j < b.size(); i++, j++) {
       int t = a[i] - '0' - borrow;
       if (j < b.size()) t = t - (b[j] - '0');
       if (t < 0) {
           borrow = 1;
           t = t + 10;
       } else borrow = 0;
       res.push_back(t);
   } 
   return  res;
 
}

int main() {
   string a, b; cin >> a >> b;
   bool sw = 0;
   if (cmp1(a, b)){
       swap(a, b);
       sw  = true;
   }
   reverse(a.begin(), a.end());
   reverse(b.begin(), b.end());
   
   vector<int> res = sub(a, b);
   if (sw) cout << "-";
   // 处理前导0
   int i = res.size() - 1; 
   while(res[i] == 0 && i > 0)i--;
   while(i >= 0) cout << res[i--];
   
   return 0;        
}

大数乘法(大数乘小数)

给定两个非负整数(不含前导 0) A 和 B,请你计算 A x B 的值

A 的长度 [1, 100000]
B 的大小 【0, 10000】

思路是,把小数B乘A的每一位,然后加起来,因为每次数位高一位,所以错位加,从A的低位到高位,每次加起来。
可以确定的是每次的乘法运算都是小数,每次有且仅有一结果位可以确认。

#include<iostream>
#include<algorithm>
using namespace std;

int main() {
  
  string a; int b;
  cin >> a >> b;
  int lastSeg = 0;
  vector<int> res;
  reverse(a.begin(), a.end());
  for (int i = 0; i < a.size(); i++) {
     int t = b * (a[i] - '0') + lastSeg;
     res.push_back(t % 10);
     lastSeg =  t / 10;
  }
  while(lastSeg > 0) {
      res.push_back(lastSeg%10);
      lastSeg /= 10;
  }
  int i = res.size() - 1;while(res[i] == 0 && i > 0) i--;
  while (i >= 0) cout << res[i--];
  return 0;    
}

大数除法(大数除小数)

除法采用试除法,每次从高位开始,除以小数,可以除就得一位商,不可补0,剩余增加一位后继续以此类推。最后不能除的是余数。
最后记得处理前导0.

#include<iostream>
#include<vector>
using namespace std;

int main() {
 string a; int b; cin >> a >> b;
 // 试除法
 vector<int> dv;
 int md = 0;
 int obj = 0;
 for (int i = 0; i < a.size(); i++) {
     obj = obj * 10 +  a[i] - '0';
     if (obj < b) {
        dv.push_back(0);
        continue;    
     }
     dv.push_back(obj/b);
     obj = obj % b;
 }
 md = obj;
 int i = 0; while(dv[i] == 0 && i < dv.size() - 1) i++; 
 while(i < dv.size()) cout << dv[i++];
 cout << endl;
 cout << md;
 return 0;
}

不足

当然考察一个成熟的工业级实现是另一个话题了,拿string来说,不同的程序组件实现都会有很多细节的考量,例如 redis的SDS,mysql的字符串实现。工程实践使用还是要多翻一下源代码,以便写出更加健壮优雅的程序实现。

参考文献

[1] https://www.acwing.com/
[2]《c++ 标准程序库》Nicolai M.Josuttis , 候捷,孟岩 译

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值