C/C++ 高精度(加减乘除)算法简单实现

news2024/11/25 16:01:44

文章目录

  • 前言
  • 一、基本原理
    • 1、存储方式
    • 2、计算方式
  • 二、辅助方法
    • 1、字符串转高精度
    • 2、整型转高精度
    • 3、比较
    • 4、打印
  • 三、算法实现
    • 1、加法
    • 2、减法
    • 3、乘法
    • 4、除法
  • 四、使用示例
    • 1、加法
    • 2、减法
    • 3、乘法
    • 4、除法
  • 总结


前言

由于上一章《C/C++ 高精度(加减乘除)算法实现》是基于工程项目考虑实现的,也做了一定的优化,实现过程较为复杂。不利于移植和使用,且比较难以理解,时间一长代码也容易忘记,所以重新编写了一个简化的版本,方便以后需要时拷贝使用。


一、基本原理

1、存储方式

采用数字记录高精度数字,数组的第一个元素存储数据长度,比如记录数字为1024示例如下:

在这里插入图片描述

2、计算方式

采用模拟立竖式计算,比如加法的计算流程,如下图所示1024+9000:
在这里插入图片描述
这里只给出加法的计算说明,其他的以此类推,减法与加法基本一致。乘法和除法略有不同,通过示例图表示也复杂,还不如通过代码去理解,本质的方法就是模拟笔算的立竖式计算。


二、辅助方法

1、字符串转高精度

长度记录在数组第一个元素中

/// <summary>
/// 通过字符串初始化
/// </summary>
/// <param name="a">[in]高精度数组</param>
/// <param name="value">[in]字符串首地址</param>
static void loadStr(int* a,const char* value)
{
    //记录长度
	a[0] = strlen(value);
	for (int i = 1; i <= a[0]; i++)
		a[i] = value[a[0] - i] - '0';
}

2、整型转高精度

/// <summary>
/// 通过无符号整型初始化
/// </summary>
/// <param name="a">[in]高精度数组</param>
/// <param name="value">[in]整型值</param>
static void loadInt(int* a, uint64_t value)
{
	for (size_t i = 1; i < 8096; i++)
	{
		a[i] = value % 10;
		value /= 10;
		if (!value)
		{   
		    //记录长度
			a[0] = i;
			return;
		}
	}
}

3、比较

/// <summary>
/// 比较两个高精度数的大小
/// </summary>
/// <param name="a">[in]第一个数</param>
/// <param name="b">[in]第二个数</param>
/// <returns>1是a>b,0是a==b,-1是a<b</returns>
static int compare(int* a, int* b)
{
	if (a[0] > b[0])return 1;
	if (a[0] < b[0])return -1;
	for (int i = a[0]; i > 0; i--)
		if (a[i] > b[i])return 1;
		else if (a[i] < b[i])return -1;
	return 0;
}

4、打印

/// <summary>
/// 打印输出结果
/// </summary>
static void print(int* a) {
	if (!a[0])
		printf("0");
	for (int i = a[0]; i > 0; i--)
		printf("%d", a[i]);
}

三、算法实现

原理就不做具体介绍了,四种计算的核心都是模拟立竖式计算。

1、加法

为了保证代码相对简单,当b长度较小时可能会做一些多余的计算,不影响结果。

/// <summary>
/// 加法(累加)
///结果会保存在a中
/// </summary>
/// <param name="a">[in]被加数</param>
/// <param name="b">[in]加数</param>
static	void acc(int* a, int* b)
{
	int len = a[0] > b[0] ? a[0] : b[0];
	memset(a + a[0] + 1, 0, (len - a[0] + 1) * sizeof(int));
	memset(b + b[0] + 1, 0, (len - b[0] + 1) * sizeof(int));
	for (int i = 1; i <= len; i++) {
		int temp = a[i] + b[i];
		a[i] = temp % 10;
		a[i + 1] += temp / 10;
	}
	if (a[len + 1])a[0]++;
}

2、减法

/// <summary>
/// 减法(累减)
///结果会保存在a中
/// </summary>
/// <param name="a">[in]被减数,被减数必须大于等于减数</param>
/// <param name="b">[in]减数</param>
static	void subc(int* a, int* b) {
	memset(b + b[0] + 1, 0, (a[0] - b[0]) * sizeof(int));
	for (int i = 1; i <= a[0]; i++)
	{
		int temp = a[i] - b[i];
		a[i] = temp;
		if (temp < 0)
		{
		    //借位
			a[i + 1] -= 1;
			a[i] += 10;
		}
	}
	//记录长度
	for (int i = a[0]; i > 0; i--)
		if (a[i])
		{
			a[0] = i;
			return;
		}
	a[0] = 0;
}

3、乘法

/// <summary>
/// 乘法
/// </summary>
/// <param name="a">[in]被乘数</param>
/// <param name="b">[in]乘数</param>
/// <param name="c">[out]结果,数组长度必须大于等于aLen+bLen+1</param>
static	void mul(int* a, int* b, int c[]) {
	c[a[0] + b[0]] = 0;
	memset(c, 0, sizeof(int) * (a[0] + b[0] + 1));
	for (int i = 1; i <= a[0]; i++)
	{
		int j;
		int d = 0;
		//被乘数的一位去乘以乘数的每一位
		for (j = 1; j <= b[0]; j++)
		{
			int temp = a[i] * b[j] + c[j + i - 1] + d;
			c[j + i - 1] = temp % 10;
			d = temp / 10;
		}
		if (d)
		{
			c[j + i - 1] = d;
		}
	}
	//记录长度
	for (int i = a[0] + b[0]; i > 0; i--)
		if (c[i])
		{
			c[0] = i;
			return;
		}
}

4、除法

采用了升阶+减法实现

/// <summary>
/// 除法
/// 依赖减法subc
/// </summary>
/// <param name="a">[in]被除数,被除数必须大于除数</param>
/// <param name="b">[in]除数</param>
/// <param name="c">[out]商,数组长度大于等于aLen-bLen+1</param>
/// <param name="mod">[out]余数,数组长度大于等于aLen</param>>
/// <param name="temp">[in]临时缓冲区,由外部提供以提高性能,数组长度大于等于aLen-bLen+1</param>
static void divi(int* a, int* b, int* c, int* mod, int* temp) {
	//相差的阶数
	int digit = a[0] - b[0] + 1;
	memcpy(mod, a, (a[0] + 1) * sizeof(int));
	memset(c, 0, sizeof(int) * (digit + 1));
	memset(temp, 0, sizeof(int) * digit);
	while (digit)
	{
		//升阶		
		memcpy(temp + digit, b + 1, sizeof(int) * b[0]);
		temp[0] = b[0] + digit - 1;
		//减法
		while (compare(mod, temp) != -1)
		{
			subc(mod, temp);
			c[digit]++;
		}
		digit--;
	}
	//记录长度
	for (int i = a[0] - b[0] + 1; i > 0; i--)
		if (c[i])
		{
			c[0] = i;
			return;
		}
}

四、使用示例

1、加法

计算累加

int main() {
	int64_t n;
	int num[1024];
	int num2[1024];
	std::cin >> n;
	loadInt(num, 0);
	for (int64_t i = 1; i <= n; i++)
	{
		loadInt(num2, i);
		acc(num, num2);
	}
	print(num);
	return 0;
}

结果:
在这里插入图片描述

2、减法

两个任意n位数的减法,数字1大于数字2。

int main()
{
	int a1[8096], a2[8096];
	std::string s1, s2;
	std::cin >> s1 >> s2;
    loadStr(a1, s1.c_str());
    loadStr(a2, s2.c_str());
    subc(a1, a2);
	print(a1);
	return 0;
}

结果:

#数字1
752425289999999999999652142141414141414146666676667677682324000001302461646520
#数字2
587891851201874512000000000154515100202121555555555555555555555545477910232111
#计算结果
164533438798125487999652141986899041212025111121112122126768444455824551414409

3、乘法

计算阶乘

int main() {
	int64_t n;
	int num[8192];
	int num2[8192];
	int num3[8192];
	int* p1 = num;
	int* p2 = num3;
	std::cin >> n;
	loadInt(num, 1);
	for (int64_t i = 1; i <= n; i++)
	{
		loadInt(num2, i);
		mul(p1, num2, p2);
		int* temp = p1;
		p1 = p2;
		p2 = temp;
	}
	print(p1);
	return 0;
}

结果:

#阶乘数
1000
#计算结果
402387260077093773543702433923003985719374864210714632543799910429938512398629020592044208486969404800479988610197196058631666872994808558901323829669944590997424504087073759918823627727188732519779505950995276120874975462497043601418278094646496291056393887437886487337119181045825783647849977012476632889835955735432513185323958463075557409114262417474349347553428646576611667797396668820291207379143853719588249808126867838374559731746136085379534524221586593201928090878297308431392844403281231558611036976801357304216168747609675871348312025478589320767169132448426236131412508780208000261683151027341827977704784635868170164365024153691398281264810213092761244896359928705114964975419909342221566832572080821333186116811553615836546984046708975602900950537616475847728421889679646244945160765353408198901385442487984959953319101723355556602139450399736280750137837615307127761926849034352625200015888535147331611702103968175921510907788019393178114194545257223865541461062892187960223838971476088506276862967146674697562911234082439208160153780889893964518263243671616762179168909779911903754031274622289988005195444414282012187361745992642956581746628302955570299024324153181617210465832036786906117260158783520751516284225540265170483304226143974286933061690897968482590125458327168226458066526769958652682272807075781391858178889652208164348344825993266043367660176999612831860788386150279465955131156552036093988180612138558600301435694527224206344631797460594682573103790084024432438465657245014402821885252470935190620929023136493273497565513958720559654228749774011413346962715422845862377387538230483865688976461927383814900140767310446640259899490222221765904339901886018566526485061799702356193897017860040811889729918311021171229845901641921068884387121855646124960798722908519296819372388642614839657382291123125024186649353143970137428531926649875337218940694281434118520158014123344828015051399694290153483077644569099073152433278288269864602789864321139083506217095002597389863554277196742822248757586765752344220207573630569498825087968928162753848863396909959826280956121450994871701244516461260379029309120889086942028510640182154399457156805941872748998094254742173582401063677404595741785160829230135358081840096996372524230560855903700624271243416909004153690105933983835777939410970027753472000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

4、除法

给定两个非负整数A,B,请你计算 A / B的商和余数。

int main()
{
	int a1[8096], a2[8096], c[8096], mod[8096], temp[8096];
	std::string s1, s2;
	std::cin >> s1 >> s2;
	loadStr(a1, s1.c_str());
	loadStr(a2, s2.c_str());
	divi(a1, a2, c, mod, temp);
	print(c);
	std::cout << std::endl;
	print(mod);
	return 0;
}

结果:

#被除数
12458848948151231366666666666666665454545123156415641561231561213648
#除数
88484851521548496564154848456486789
#商
140802055198308817458997123299946
#余数
25178368711335236611547594127800254

总结

以上就是今天要讲的内容,本文提供的是较为简化的实现,且每个方法基本是独立的,可单独拿来使用,用法也比较简单,由于采用数组第一个元素存储长度,接口就变得很简洁,使用起来也方便了很多。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/84164.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

NetSuite资产负债表编制技巧

在最近的项目上&#xff0c;发现大家对NetSuite资产负债的编制参数缺乏足够的了解&#xff0c;导致对客户需求的支支吾吾&#xff0c;产生了沟通上的浪费。所以今朝对三个典型应用做个介绍。 1. 如何在资产负债表中选择“会计期间区间”&#xff1f; 2. 期初、发生、结余报表…

【元宇宙欧米说】Web3如何为漫画产业创造更多玩法和可能性

漫画和NFT的碰撞会产生什么火花&#xff1f;NFT如何为传统的漫画收藏增加额外价值&#xff1f;Web3时代如何为漫画带来更多玩法和可能性&#xff1f; 12月15日下午三点&#xff0c;MadManga 创始人Jun将以“Web3如何为漫画产业创造更多玩法和可能性”为题&#xff0c;与大家共…

构建文本摘要Baseline并且开始训练

构建文本摘要Baseline并且开始训练 基于前面word2vec的原理与训练实践、seq2seq模型的原理与实践以及attention机制&#xff0c;已经分别写了相关的文章来记录&#xff0c;此篇文章就是基于前面所学&#xff0c;开始着手训练文本摘要模型&#xff0c;当然仅是一个比较普通的ba…

[附源码]Node.js计算机毕业设计大学体育馆预约系统Express

项目运行 环境配置&#xff1a; Node.js最新版 Vscode Mysql5.7 HBuilderXNavicat11Vue。 项目技术&#xff1a; Express框架 Node.js Vue 等等组成&#xff0c;B/S模式 Vscode管理前后端分离等等。 环境需要 1.运行环境&#xff1a;最好是Nodejs最新版&#xff0c;我…

vue框架搭建大屏自适应方案

vue框架搭建大屏自适应方案 1.可使用flexible.js rem实现宽高&#xff0c;字体自适应 附上flexible.js代码 (function flexible(window, document) {var docEl document.documentElement;var dpr window.devicePixelRatio || 1;// adjust body font sizefunction setBody…

NocasRule负载均衡与服务实例的权重设置

NocasRule负载均衡 .yml 配置文件配置 server:port: 8080 spring:application:name: orderservicecloud:nacos:server-addr: localhost:8848 #nocas服务地址discovery:cluster-name: HZ #集群名字 userservice: #要做配置的微服务名称ribbon:NFLoadBalancerRuleClassName: com…

游戏开发57课 性能优化14

5. 内存优化 内存优化目的是加快IO&#xff0c;防止卡主线程&#xff0c;防止频繁操作&#xff08;创建/删除&#xff09;内存&#xff0c;避免内存碎片化和占用过高。 5.1 缓存法 与CPU的缓存计算类似&#xff0c;思路是将需要重复创建的对象缓存起来&#xff0c;销毁时将它…

安装、启动与停止Apache服务

安装、启动与停止Apache服务 安装Apache相关软件 [rootcentos7 ~]# rpm -q httpd [rootcentos7-1 ~]# mkdir /opt/centos //创建目录/opt/centos [rootcentos7-1 ~]# mount /dev/cdrom /opt/centos //挂载光盘到/opt/centos 下 mount: /dev/sr0 写保护…

Spring Boot 3.0.0正式发布,Banner不再支持图片增强可观测性

本文已被https://yourbatman.cn收录&#xff1b;女娲Knife-Initializr工程可公开访问啦&#xff1b;程序员专用网盘https://wangpan.yourbatman.cn&#xff1b;技术专栏源代码大本营&#xff1a;https://github.com/yourbatman/tech-column-learning&#xff1b;公号后台回复“…

openCV(一)基础背景

1 认识计算机视觉 2012年AlexNet模型在ImageNet图像分类中获得比赛冠军&#xff0c;深度学习开始在计算机视觉领域流行。早期的计算机视觉主要集中在重建方面&#xff0c;2012年以后在感知和重建两个领域都受到了深度学习的影响。应用场景包括自动驾驶、机器视觉、安防监控、其…

猿如意中的【PostgreSQL 数据库】工具详情介绍

猿如意中的【PostgreSQL 数据库】工具详情介绍 一、工具名称 PostgreSQL 数据库 二、下载安装渠道 PostgreSQL 数据库V14.2 通过CSDN官方开发的【猿如意】客户端进行下载安装。 2.1 什么是猿如意&#xff1f; 猿如意是一款面向开发者的辅助开发工具箱&#xff0c;包含了效…

jenkins-pipeline与变量

本文介绍如何在pipeline中使用变量 使用jenkins预定义的环境变量 jenkins预先定义了一些环境变量&#xff0c;在pipeline中使用${env.key}来调用 另外安装了第三方插件&#xff0c;会有新的环境变量&#xff0c;可以使用插件Environment Inject来查看 在pipeline中使用预定义…

Java二维数组项目练习

T1.显示所有书店客户的信息 示例代码 public static void main(String[] args) {String[][] users{{"1100","18","100"},{"1101","24","834"},{"1102","13","20000"},{"1103…

软件测试——用例篇

文章目录为什么在测试前要设计测试用例基于需求设计测试用例等价类边界值错误猜测法场景法因果图正交法为什么在测试前要设计测试用例 测试用例是执行测试的依据。可以复用&#xff08;回归测试的时候&#xff09;衡量需求的覆盖率自动化测试的依据有借鉴意义&#xff0c;后续…

OH----原子量的妙用--保护usb时序

1、问题&#xff1a; 展锐平台&#xff0c;usb otg高概率不能正确检测识别到 2、思路&#xff1a; usb使用musb控制器&#xff0c;展锐的平台处理代码是musb_sprd.c&#xff0c;在这个文件中对usb mode做检测和切换&#xff0c;log级别跳到最高&#xff0c;在probe中的关键函…

用 Taichi 加速 Python:提速 100+ 倍!

Python 已经成为世界上最流行的编程语言&#xff0c;尤其在深度学习、数据科学等领域占据主导地位。但是由于其解释执行的属性&#xff0c;Python 较低的性能很影响它在计算密集&#xff08;比如多重 for 循环&#xff09;的场景下发挥作用&#xff0c;实在让人又爱又恨。如果你…

PAT(乙级)2022年冬季考试

此前先后花了十元去做了乙级题&#xff0c;从最开始分别是70&#xff0c;35&#xff0c;43&#xff0c;33&#xff08;途中做了RobpCom,只搞定了签到题&#xff09;&#xff0c;想着报今年的冬季赛&#xff0c;但是报名费有点高啊&#xff0c;加上做下来感觉不怎么样&#xff0…

[附源码]Python计算机毕业设计Django的黄河文化科普网站

项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等等。 环境需要 1.运行环境&#xff1a;最好是python3.7.7&#xff0c;…

GitHub搜索开源项目

GitHub的流行&#xff0c; GitHub在开源世界的受欢迎程度自不必多言。再加上今天&#xff0c;GitHub官方又搞了个大新闻&#xff1a;私有仓库也改为免费使用&#xff0c;这在原来可是需要真金白银的买的。可见微软收购后&#xff0c;依然没有改变 GitHub 的定位&#xff0c;甚至…

使用高德地图展示点位和信息窗体展示数据及播放视频

使用高德地图做了一个在地图展示点位&#xff0c;并通过点击&#xff0c;显示直播的功能&#xff0c;这个任务是为了之后大屏做准备。 这是一个能展示多个点标记&#xff0c;并在点击的时候弹出信息窗体&#xff0c;并在信息窗体中播放视频&#xff0c;且展示相关信息以及操作…