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


前言

由于上一章《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){
    
    
	a[0] = 0;
	while (value)a[++a[0]]= value % 10,value/=10;
}

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>
/// <param name="a">[in]源</param>
/// <param name="b">[in]目标</param>
static void copy(int* a, int* b) {
    
    
	memcpy(b, a, (a[0] + 1) * sizeof(int));
}

5、转字符串

/// <summary>
/// 输出到字符串,
/// </summary>
/// <param name="a">[in]高精度数组</param>
/// <param name="str">[out]字符串,由外部停供缓冲区,需要保证长度足够</param>
static void toStr(int* a, char* str) {
    
    
	if (!a[0]){
    
    
		sprintf(str, "0");
		return;
	}
	for (int i = a[0]; i > 0; i--)str[a[0] - i] = a[i] + '0';
	str[a[0]] = '\0';
}

6、打印

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

三、算法实现

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

1、加法

为了保证代码相对简单同时确保性能,采用了加减替换除法和取余。

/// <summary>
/// 加
/// </summary>
/// <param name="a">[in]被加数</param>
/// <param name="b">[in]加数</param>
/// <param name="c">[out]结果</param>
static	void plus(int* a, int* b, int* c) {
    
    
	int* p;
	if (a[0] < b[0])p = a, a = b, b = p;//确保a长度最大	
	int i = 1, alen = a[0], blen = b[0];
	c[0] = c[alen + 1] = 0;
	if (a != c)memcpy(c + blen + 1, a + blen + 1, (alen - blen) * sizeof(int));//a多出的部分直接拷贝到结果
	for (; i <= blen; i++) {
    
    
		c[i] = a[i] + b[i];
		if (c[i - 1] > 9)c[i - 1] -= 10, c[i]++;//判断上一位是否进位	
	}
	i--;
	while (c[i++] >9)c[i - 1] -= 10, c[i]++;//继续判断进位
	c[0] = c[alen + 1] ? alen + 1 : alen;//记录长度
}
/// <summary>
/// 加等于
///结果会保存在a中
/// </summary>
/// <param name="a">[in]被加数</param>
/// <param name="b">[in]加数</param>
static	void plusq(int* a, int* b) {
    
    
	plus(a, b, a);
}

2、减法

/// <summary>
/// 减
/// </summary>
/// <param name="a">[in]被减数,被减数必须大于等于减数</param>
/// <param name="b">[in]减数</param>
/// <param name="c">[out]结果</param>
static	void sub(int* a, int* b, int* c) {
    
    
	int i = 1, alen = a[0];
	if (a != c)memcpy(c + b[0] + 1, a + b[0] + 1, (a[0] - b[0]) * sizeof(int));//a多出的部分直接拷贝到结果
	c[0] = 1;
	for (; i <= b[0]; i++) {
    
    
		c[i] = a[i] - b[i];
		if (c[i - 1] < 0)c[i - 1] += 10, c[i] --;//判断上一位是否补位		
	}
	i--;
	while (c[i++] < 0)c[i - 1] += 10, c[i]--;//继续判断补位	
	while (!c[alen--]); c[0] = alen + 1;//记录长度
}
/// <summary>
/// 减法等于
///结果会保存在a中
/// </summary>
/// <param name="a">[in]被减数,被减数必须大于等于减数</param>
/// <param name="b">[in]减数</param>
static	void subq(int* a, int* b) {
    
    
	sub(a, b, a);
}

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[]) {
    
    
	int len = a[0] + b[0], d = 0;
	memset(c, 0, sizeof(int) * (len + 1));
	b[b[0] + 1] = 0; c[0] = 1;//防止越界
	for (int i = 1; i <= a[0]; i++)
		for (int j = 1; j <= b[0] + 1; j++)
			c[j + i - 1] += a[i] * b[j] + d, d = c[j + i - 1] / 10, c[j + i - 1] %= 10;
	while (!c[len])len--; c[0] = len;
}
/// <summary>
/// 乘等于
/// 累乘,结果存放于a
/// </summary>
/// <param name="a">[in]被乘数,数组长度必须大于等于2aLen+bLen+1</param>
/// <param name="b">[in]乘数</param>
static	void mulq(int* a, int* b) {
    
    
	int* c = a + a[0] + b[0] + 1;
	memcpy(c, a, (a[0] + 1) * sizeof(int));
	mul(c, b, a);
}

4、除法

采用了升阶+减法实现

/// <summary>
/// 除法
/// 依赖减法subq、compare
/// </summary>
/// <param name="a">[in]被除数,被除数必须大于除数</param>
/// <param name="b">[in]除数</param>
/// <param name="c">[out]商,数组长度大于等于3aLen-bLen+1</param>
/// <param name="mod">[out]余数,可以为NULL,数组长度大于等于aLen</param>>
static void div(int* a, int* b, int* c, int* mod) {
    
    
	int len = a[0] - b[0] + 1;//相差的阶数
	int* temp = c + a[0] + 1;//缓冲区
	if (!mod)mod = temp + a[0] + 1;//缓冲区
	memcpy(mod, a, (a[0] + 1) * sizeof(int));
	memset(c, 0, sizeof(int) * (len + 1));
	memset(temp, 0, sizeof(int) * len);
	c[0] = 1;//防止while越界
	for (int i = len; i > 0; i--) {
    
    
		memcpy(temp + i, b + 1, sizeof(int) * b[0]);	//升阶	
		temp[0] = b[0] + i - 1;
		while (compare(mod, temp) != -1)subq(mod, temp), c[i]++;	//减法
	}
	while (!c[len])len--; c[0] = len;
}
/// <summary>
/// 除等于
/// 商保存在a
/// 依赖div
/// </summary>
/// <param name="a">[in]被除数,被除数必须大于除数</param>
/// <param name="b">[in]除数</param>
/// <param name="mod">[out]余数,可以为NULL,数组长度大于等于aLen</param>>
static void divq(int* a, int* b, int* mod) {
    
    
	div(a, b, a, mod);
}

四、完整代码

上面已经展示了全部代码,这里整合到一起只是方便拷贝。

#include<stdio.h>
#include<string.h>
#include<math.h>
#include<stdint.h>
/// <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';
}
/// <summary>
/// 输出到字符串,
/// </summary>
/// <param name="a">[in]高精度数组</param>
/// <param name="str">[out]字符串,由外部停供缓冲区,需要保证长度足够</param>
static void toStr(int* a, char* str) {
    
    
	if (!a[0]){
    
    
		sprintf(str, "0");
		return;
	}
	for (int i = a[0]; i > 0; i--)str[a[0] - i] = a[i] + '0';
	str[a[0]] = '\0';
}
/// <summary>
/// 通过无符号整型初始化
/// </summary>
/// <param name="a">[in]高精度数组</param>
/// <param name="value">[in]整型值</param>
static void loadInt(int* a, uint64_t value) {
    
    
	a[0] = 0;
	while (value)a[++a[0]] = value % 10, value /= 10;
}
/// <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;
}
/// <summary>
/// 复制
/// </summary>
/// <param name="a">[in]源</param>
/// <param name="b">[in]目标</param>
static void copy(int* a, int* b) {
    
    
	memcpy(b, a, (a[0] + 1) * sizeof(int));
}
/// <summary>
/// 打印输出结果
/// </summary>
/// <param name="a">[in]高精度数组</param>
static void print(int* a) {
    
    
	if (!a[0])printf("0");
	for (int i = a[0]; i > 0; i--)printf("%d", a[i]);
}

/// <summary>
/// 加
/// </summary>
/// <param name="a">[in]被加数</param>
/// <param name="b">[in]加数</param>
/// <param name="c">[out]结果</param>
static	void plus(int* a, int* b, int* c) {
    
    
	int* p;
	if (a[0] < b[0])p = a, a = b, b = p;//确保a长度最大	
	int i = 1, alen = a[0], blen = b[0];
	c[0] = c[alen + 1] = 0;
	if (a != c)memcpy(c + blen + 1, a + blen + 1, (alen - blen) * sizeof(int));//a多出的部分直接拷贝到结果
	for (; i <= blen; i++) {
    
    
		c[i] = a[i] + b[i];
		if (c[i - 1] > 9)c[i - 1] -= 10, c[i]++;//判断上一位是否进位	
	}
	i--;
	while (c[i++] > 9)c[i - 1] -= 10, c[i]++;//继续判断进位
	c[0] = c[alen + 1] ? alen + 1 : alen;//记录长度
}
/// <summary>
/// 加等于
///结果会保存在a中
/// </summary>
/// <param name="a">[in]被加数</param>
/// <param name="b">[in]加数</param>
static	void plusq(int* a, int* b) {
    
    
	plus(a, b, a);
}

/// <summary>
/// 减
/// </summary>
/// <param name="a">[in]被减数,被减数必须大于等于减数</param>
/// <param name="b">[in]减数</param>
/// <param name="c">[out]结果</param>
static	void sub(int* a, int* b, int* c) {
    
    
	int i = 1, alen = a[0];
	if (a != c)memcpy(c + b[0] + 1, a + b[0] + 1, (a[0] - b[0]) * sizeof(int));//a多出的部分直接拷贝到结果
	c[0] = 1;
	for (; i <= b[0]; i++) {
    
    
		c[i] = a[i] - b[i];
		if (c[i - 1] < 0)c[i - 1] += 10, c[i] --;//判断上一位是否补位		
	}
	i--;
	while (c[i++] < 0)c[i - 1] += 10, c[i]--;//继续判断补位	
	while (!c[alen--]); c[0] = alen + 1;//记录长度
}
/// <summary>
/// 减法等于
///结果会保存在a中
/// </summary>
/// <param name="a">[in]被减数,被减数必须大于等于减数</param>
/// <param name="b">[in]减数</param>
static	void subq(int* a, int* b) {
    
    
	sub(a, b, a);
}

/// <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[]) {
    
    
	int len = a[0] + b[0], d = 0;
	memset(c, 0, sizeof(int) * (len + 1));
	b[b[0] + 1] = 0; c[0] = 1;//防止越界
	for (int i = 1; i <= a[0]; i++)
		for (int j = 1; j <= b[0] + 1; j++)
			c[j + i - 1] += a[i] * b[j] + d, d = c[j + i - 1] / 10, c[j + i - 1] %= 10;
	while (!c[len])len--; c[0] = len;
}
/// <summary>
/// 乘等于
/// 累乘,结果存放于a
/// </summary>
/// <param name="a">[in]被乘数,数组长度必须大于等于2aLen+bLen+1</param>
/// <param name="b">[in]乘数</param>
static	void mulq(int* a, int* b) {
    
    
	int* c = a + a[0] + b[0] + 1;
	memcpy(c, a, (a[0] + 1) * sizeof(int));
	mul(c, b, a);
}

/// <summary>
/// 除法
/// 依赖减法subeq
/// </summary>
/// <param name="a">[in]被除数,被除数必须大于除数</param>
/// <param name="b">[in]除数</param>
/// <param name="c">[out]商,数组长度大于等于3aLen-bLen+1</param>
/// <param name="mod">[out]余数,可以为NULL,数组长度大于等于aLen</param>>
static void div(int* a, int* b, int* c, int* mod) {
    
    
	int len = a[0] - b[0] + 1;//相差的阶数
	int* temp = c + a[0] + 1;//缓冲区
	if (!mod)mod = temp + a[0] + 1;//缓冲区
	memcpy(mod, a, (a[0] + 1) * sizeof(int));
	memset(c, 0, sizeof(int) * (len + 1));
	memset(temp, 0, sizeof(int) * len);
	c[0] = 1;//防止while越界
	for (int i = len; i > 0; i--) {
    
    
		memcpy(temp + i, b + 1, sizeof(int) * b[0]);	//升阶	
		temp[0] = b[0] + i - 1;
		while (compare(mod, temp) != -1)subq(mod, temp), c[i]++;	//减法
	}
	while (!c[len])len--; c[0] = len;
}
/// <summary>
/// 除等于
/// 商保存在a
/// 依赖div
/// </summary>
/// <param name="a">[in]被除数,被除数必须大于除数</param>
/// <param name="b">[in]除数</param>
/// <param name="mod">[out]余数,可以为NULL,数组长度大于等于aLen</param>>
static void divq(int* a, int* b, int* mod) {
    
    
	div(a, b, a, mod);
}

五、使用示例

1、加法

两个任意n位数的加法

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

结果:

#数字1
5878748967498789787777777777777777777777777777777777884515245788787
#数字2
11111111111115209615261203032023165144654654897489512121264
#计算结果
5878748978609900898892987393038980809800942922432432782004757910051

2、减法

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

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

结果:

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

3、乘法

计算阶乘

int main() {
    
    
	int64_t n;
	int a[8192];
	int b[8192];
	std::cin >> n;
	loadInt(a, 1);
	for (int64_t i = 1; i <= n; i++) {
    
    
		loadInt(b, i);
		mulq(a, b);
	}
	print(a);
	return 0;
}

结果:

#阶乘数
1000
#计算结果
402387260077093773543702433923003985719374864210714632543799910429938512398629020592044208486969404800479988610197196058631666872994808558901323829669944590997424504087073759918823627727188732519779505950995276120874975462497043601418278094646496291056393887437886487337119181045825783647849977012476632889835955735432513185323958463075557409114262417474349347553428646576611667797396668820291207379143853719588249808126867838374559731746136085379534524221586593201928090878297308431392844403281231558611036976801357304216168747609675871348312025478589320767169132448426236131412508780208000261683151027341827977704784635868170164365024153691398281264810213092761244896359928705114964975419909342221566832572080821333186116811553615836546984046708975602900950537616475847728421889679646244945160765353408198901385442487984959953319101723355556602139450399736280750137837615307127761926849034352625200015888535147331611702103968175921510907788019393178114194545257223865541461062892187960223838971476088506276862967146674697562911234082439208160153780889893964518263243671616762179168909779911903754031274622289988005195444414282012187361745992642956581746628302955570299024324153181617210465832036786906117260158783520751516284225540265170483304226143974286933061690897968482590125458327168226458066526769958652682272807075781391858178889652208164348344825993266043367660176999612831860788386150279465955131156552036093988180612138558600301435694527224206344631797460594682573103790084024432438465657245014402821885252470935190620929023136493273497565513958720559654228749774011413346962715422845862377387538230483865688976461927383814900140767310446640259899490222221765904339901886018566526485061799702356193897017860040811889729918311021171229845901641921068884387121855646124960798722908519296819372388642614839657382291123125024186649353143970137428531926649875337218940694281434118520158014123344828015051399694290153483077644569099073152433278288269864602789864321139083506217095002597389863554277196742822248757586765752344220207573630569498825087968928162753848863396909959826280956121450994871701244516461260379029309120889086942028510640182154399457156805941872748998094254742173582401063677404595741785160829230135358081840096996372524230560855903700624271243416909004153690105933983835777939410970027753472000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

4、除法

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

int main()
{
    
    
	int a[1024], b[1024], mod[1024];
	std::string s1, s2;
	std::cin >> s1 >> s2;
	loadStr(a, s1.c_str());
	loadStr(b, s2.c_str());
	divq(a, b, mod);
	print(a);
	std::cout << std::endl;
	print(mod);
	return 0;
}

结果:

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

总结

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

猜你喜欢

转载自blog.csdn.net/u013113678/article/details/128289724