C语言——高精度乘法

一、引子

高精度乘法相较于高精度加法和减法有更多的不同,加法和减法是一位对应一位进行操作的,而乘法是一个数的每一位对另一个数的每一位进行操作,需要的计算步骤更多。

二、核心算法

void Calculate(int num1[], int num2[], int numres[], int len1, int len2)
{
	// 核心乘法算法
	for (int i = 0; i < len1; i++)
	{
		for (int j = 0; j < len2; j++)
		{
			numres[i + j] += num1[i] * num2[j];
			numres[i + j + 1] += numres[i + j] / 10;
			numres[i + j] = numres[i + j] % 10;
		}
	}
}

这一段代码是高精度乘法核心算法的实现,它模拟了我们手工进行多位数乘法的过程。这里,我们有两个数n1和n2,它们分别以字符串的形式存储,并被转换成整数数组num1和num2,其中低位在前,高位在后(即字符串“123”会被存储成整数数组{3, 2, 1})。然后我们对这两个数组执行乘法。

对于乘法,我们对于num1的每一位(由外层循环 i 控制)与num2的每一位(由内层循环 j 控制)相乘。由于我们是按位计算,所以需要考虑结果的位数(res数组的下标 i + j )。这里考虑结果的位数是最低位数,由于数组的特性,两个数字的最低位在数组中是作为第零位的,而且在数组中两个数的每一位相当于之前都是减少了一位,如果是10 * 10两个两位数相乘理论上结果是三位,但是如果将两数字的位数相加作为结果的位数,那结果就是四(2 + 2)位数,这样就不对了,但是由于数组的特性,将两数字位数相加改成两数字当前索引相加就可以了,10 * 10,索引相加是(1 + 1) = 2,而二作为索引是3位数,结果是三位数,这样就对了。在这里的原理是转换的过程,两个数的位数转成索引要减一,两个数就是减二,而结果的索引转成位数又要加一,这是就相当于减一,在前面直接用位数运算的版本中,结果是四位,这里的就相当于结果的位数减一,结果的位数就变成了三,就对了。

假设两个两位数相乘,结果的位数最低可能是三位,最高是四位,这里是按最低算的。如果放到了最高位就会导致这一步的结果扩大了十倍,对比上面的解释我们发现数组是可以直接实现将每一步结果存到正确的位上的。也就是num1[i] * num2[j]的结果可以直接放到res[i + j]里的这样是正确的。

如果不使用数组而使用数字位数的话,也就是假设 i 和 j 不是索引而是数位,那就不能将(num1的 i 位的数字) * (num2的 j 位的数字)的结果放到res的(i + j)位上,而要把结果放到res的(i + j - 1)位上。

假设是10 * 10 = 100,结果是三位,

num1[0] * num2[0]是0 * 0 = 0,res[0 + 0] = 0,res[0] = 0

num1[1] * num2[0] = 0,res[1 + 0] = 0,res[1] = 0

num1[0] * num2[1] = 0,res[0 + 1] = 0,res[1] = 0

num1[1] * num[1] = 1,res[1 + 1] = 1,res[2] = 1

这是结果就是100

这个乘法的过程是:

1. res[i + j] += n1[i] * n2[j]:这里我们将num1的第i位和num2的第 j 位相乘,然后加到结果的相应位置。由于n1和n2的下标为 i 和 j ,那么对应的结果应该是res[i + j]。这里的结果为res[i + j]就是我们上面解释的体现,用数组的特性使得这样做是正确的。

2. res[i + j + 1] += res[i + j] / 10:这里我们处理进位。如果res[i + j]的结果是两位数(即大于或等于10),我们需要将十位上的数字进位到结果的下一个位置res[i + j + 1]。

3. res[i + j] = res[i + j] % 10:保证结果数组res的每一位都是个位数,即进行模10操作。进位已经在上一步处理过了,这里确保数组res中存储的是当前位的正确数字。

这个过程会一直重复,直到num1和num2中的每一位都相乘。由于进位可能影响到最终结果的位数,结果数组res的实际使用长度可能会比num1和num2的长度总和还要大。所以,我们在定义res数组的时候要确保它有足够的空间来存储可能的最大结果,即num1长度和num2长度的和。

在乘法完成后,结果数组res中存储的是乘法结果的每一位数字,但是顺序是反的,即最低位在数组的第0个位置。在最后,我们需要将结果数组转换回字符串,并且反转回正确的顺序来输出最终的乘积。

三、处理正负

我们同时还要考虑结果的正负,对结果的正负进行判断,影响结果正负的因素是乘数的正负,这里就是同正异负,可以判断好结果的正负,然后将负号在最后打印。

在输入乘数时会有负号,所以要判断乘数是否为负数,然后还要将乘数前的负号去除,防止对之后的计算产生影响。

同时,在处理正负前不能将字符数组反转或者转成整型数组。

int JudgePorN(char arrch1[], char arrch2[])//1代表负,0代表正
{
	if (arrch1[0] == '-' && arrch2[0] != '-')
	{
		arrch1[0] = '0';//将 - (负号)替换为符号0,防止对后面的计算有影响,符号0在后面的计算中不会有影响,因为零会在后面作为前导零去除。注意是'0',如果写成数字0就不对了,数字0是\0的ASIIC码,写成数字0会被转换成\0,\0是字符串的结束符
		return 1;
	}
	else if (arrch1[0] != '-' && arrch2[0] == '-')
	{
		arrch2[0] = '0';
		return 1;
	}
	else if (arrch1[0] == '-' && arrch2[0] == '-')
	{
		arrch1[0] = '0';
		arrch2[0] = '0';
		return 0;
	}
	return 0;
}

所以处理正负函数要在字符数组反转函数和转成整型数组函数之前调用。

四、代码实现

#include <stdio.h>
#include <string.h>

void DigitReverse(char arr[])//反转字符串,以便后续计算
{
	int length = (int)strlen(arr);
	for (int i = 0; i < length / 2; i++)
	{
		int temp = arr[i];
		arr[i] = arr[length - i - 1];
		arr[length - i - 1] = temp;
	}
}

void StringTranstoNumber(char arrchar[],int arrnum[])//将字符数组转换为数字数组
{
	int length = (int)strlen(arrchar);
	for (int i = 0; i < length; i++)
	{
		arrnum[i] = arrchar[i] - '0';
	}
}

void Calculate(int num1[], int num2[], int numres[], int len1, int len2)
{
	// 核心乘法算法
	for (int i = 0; i < len1; i++)
	{
		for (int j = 0; j < len2; j++)
		{
			numres[i + j] += num1[i] * num2[j];
			numres[i + j + 1] += numres[i + j] / 10;
			numres[i + j] = numres[i + j] % 10;
		}
	}
}

void BigNumMul(char arrch1[], char arrch2[], int res[],int len1,int len2)
{
	DigitReverse(arrch1);
	DigitReverse(arrch2);

	int num1[505] = {0};
	int num2[505] = {0};

	StringTranstoNumber(arrch1,num1);
	StringTranstoNumber(arrch1,num2);

	Calculate(num1,num2,res,len1,len2);//计算结果
}

void Print(int resnum[],int lengthmax)//打印函数
{
	int i = lengthmax;
	while (i > 0 && resnum[i] == 0)//去除前导零,因为是按最大位数算的,可能有前导零
	{
		i--;
	}
	for (; i >= 0; i--)
	{
		printf("%d",resnum[i]);
	}
} 

int JudgePorN(char arrch1[], char arrch2[])//1代表负,0代表正
{
	if (arrch1[0] == '-' && arrch2[0] != '-')
	{
		arrch1[0] = '0';//将 - (负号)替换为符号0,防止对后面的计算有影响,符号0在后面的计算中不会有影响,因为零会在后面作为前导零去除。注意是'0',如果写成数字0就不对了,数字0是\0的ASIIC码,写成数字0会被转换成\0,\0是字符串的结束符
		return 1;
	}
	else if (arrch1[0] != '-' && arrch2[0] == '-')
	{
		arrch2[0] = '0';
		return 1;
	}
	else if (arrch1[0] == '-' && arrch2[0] == '-')
	{
		arrch1[0] = '0';
		arrch2[0] = '0';
		return 0;
	}
	return 0;
}

int main()
{
	char ch1[505] = "0";
	char ch2[505] = "0";

	scanf("%s",ch1);
	scanf("%s",ch2);

	int res[1010] = {0};

	int len1 = (int)strlen(ch1);
	int len2 = (int)strlen(ch2);

	if (len1 == 1 && ch1[0] == '0')
	{
		printf("0");
	}
	else if (len2 == 1 && ch2[0] == '0')
	{
		printf("0");
	}
	else if (len1 == 1 && len2 == 1 && ch1[0] == '0' && ch2[0] == '0')
	{
		printf("0");
	}
	else//非零情况
	{
		int judgevalue = JudgePorN(ch1, ch2);//判断结果正负并去除字符串前面的 - (负号)

		BigNumMul(ch1, ch2, res, len1, len2);

		int lengthmax = len1 + len2;//乘法结果最大可能是两个乘数的位数和

		if (judgevalue == 1)//结果为负
		{
			printf("-");
			Print(res, lengthmax);
		}
		else//结果为正
		{
			Print(res, lengthmax);
		}
	}
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Linux docker安装nacos

1&#xff1a;首先下载安装docker&#xff0c;这里不做描述&#xff0c;可以自行百度安装。 2&#xff1a;通过docker下载nacos&#xff0c; docker pull nacos/nacos-server:latest3&#xff1a;搭建临时nacos容器&#xff0c;此步骤的目的是为了获取nacos的配置文件和日志 …

总结两套JVM模版配置

大白话&#xff1a; 1.秒杀场景&#xff0c;Eden会设置的比较大&#xff1b; 2.FullGC是代价最高的GC&#xff0c;频率越低越好。 大白话&#xff1a; 一般情况下&#xff0c;设置JVM堆内存为物理机内存的一半&#xff0c;最大不超过3/4; -Xmn3072M - 设置新生代的内存大小&a…

『 C++ 』二叉树进阶OJ题

文章目录 根据二叉树创建字符串 &#x1f996;&#x1f969; 题目描述&#x1f969; 解题思路&#x1f969; 代码 二叉树的层序遍历(分层遍历) &#x1f996;&#x1f969; 题目描述&#x1f969; 解题思路&#x1f969; 代码 二叉树的层序遍历(分层遍历)Ⅱ &#x1f996;&…

uniapp websocket的使用和封装

在uniapp中socket分为两种形式&#xff0c;第一种适用于只有一个socket链接&#xff0c;第二种适用于多个socket链接。传送门 这里以socketTask为列子封装 在utils新建一个文件 在你要使用的页面引入&#xff0c;我这是聊天那种&#xff0c;所以我在拿到用户信息之后连接sock…

【STM32单片机入门-1】堆栈/全局变量,局部变量,静态全局变量,局部静态变量等

1&#xff0c;堆栈对比 堆&#xff1a;由程序员分配和释放。容易产生碎片&#xff0c;使用方便&#xff0c;地址分配使用从下到上 栈&#xff1a;用来存放函数地址和局部参数&#xff0c;主函数使用时&#xff0c;要对函数的首地址断点保存&#xff0c;地址分配从上到下&#…

微软官方发布的C#开源、免费、实用的Windows工具箱

前言 今天分享一款由微软官方发布的C#开源、免费、实用的Windows工具箱&#xff08;帮助用户调整和简化Windows系统的体验&#xff0c;从而提高工作效率&#xff09;&#xff1a;Microsoft PowerToys。 项目介绍 Microsoft PowerToys 是使用 C 和 C# 编程语言开发的。它利用了 …

ansible的playbook

1、playbook的组成部分 &#xff08;1&#xff09;task任务&#xff1a;在目标主机上执行的操作&#xff0c;使用模块定义这些操作&#xff0c;每个任务都是一个模块的调用 &#xff08;2&#xff09;variables变量&#xff1a;存储和传递数据&#xff08;变量可以自定义&…

DRF从入门到精通二(Request源码分析、DRF之序列化组件)

文章目录 一、Request对象源码分析区分原生request和新生request新的request还能像原来的reqeust一样使用吗源码片段分析总结&#xff1a; 二、DRF之序列化组件序列化介绍序列化步骤序列化组件的基本使用反序列化基本使用反序列化的新增反序列化的新增删除单条 反序列化的校验 …

天猫数据分析(天猫查数据工具):2023年天猫平台假发行业市场销售数据分析报告

如今&#xff0c;由于人们工作和生活的压力较大&#xff0c;居民脱发问题严重&#xff0c;且脱发群体倾向于80后和90后&#xff0c;逐渐向低龄化发展。除脱发外&#xff0c;在颜值经济的背景下&#xff0c;人们越来越注重外貌和形象&#xff0c;假发作为一种改善发型的工具&…

Graylog配置日志保留策略

找了半天没找到说的清楚的&#xff0c;只能抠官方文档 graylog的归档&#xff08;日志持久化&#xff09;只有付费版才能用&#xff0c;所以日志只能存在es中 1.理解官方给出的几个概念 轮转策略 (Index Rotation Strategy): 轮转策略定义了何时创建新的索引以及何时关闭旧的索…

ssm基于vue技术的绿色蔬菜销售管理系统+vue论文

摘 要 现代经济快节奏发展以及不断完善升级的信息化技术&#xff0c;让传统数据信息的管理升级为软件存储&#xff0c;归纳&#xff0c;集中处理数据信息的管理方式。本绿色蔬菜销售管理就是在这样的大环境下诞生&#xff0c;其可以帮助管理者在短时间内处理完毕庞大的数据信息…

网络技术基础与计算思维实验教程_2.2_单交换机实验_重制版

实验内容 实验目的 实验原理 关键命令说明 开始实验 构建 选择交换机 选择终端--台式机 放置四台终端 直通线连接 依次连接pc0到pc3 终端配置Ip地址和子网掩码 完成了交换机和终端连接以后,为每一个终端配置Ip地址和子网掩码 单击pc0 在选择桌面选项卡中选择Ip配置使用程序 …

快速从图中提取曲线坐标数据的在线工具WebPlotDigitizer

快速从图中提取曲线坐标数据的在线工具WebPlotDigitizer 1 介绍2 WebPlotDigitizer在线版的使用2.1 上传图像2.2 点击横纵坐标点&#xff1a;2.3 选择曲线 3 查看数据参考 1 介绍 写论文时要对比别人曲线图、点图、柱形图的数据&#xff0c;但是只有图没有原始数据怎么办&…

【51单片机系列】C51中的中断系统扩展实验

本文是关于51单片机中断系统的扩展实验。 文章目录 一、 扩展实验一&#xff1a;使用外部中断0控制蜂鸣器&#xff0c;外部中断1控制直流电机二、扩展实验二&#xff1a;修改定时器初值&#xff0c;设定3秒钟的定时时间让LED模块闪烁三、扩展实验三&#xff1a;使用定时器1和数…

基于NestJS 和 TypeORM 实现 CURD RESTful API接口

前言 对于服务端项目而言&#xff0c;对外如何提供合格规范的HTTP接口&#xff0c;对内如何优雅的操作数据存储&#xff0c;比如mysql、mongodb。 本文是NestJS服务端开发的基础入门教程&#xff0c;我会根据成熟的解决方案&#xff0c;给大家详细介绍如何基于NestJS实现开发…

【RTOS学习】源码分析(信号量和互斥量 事件组 任务通知)

&#x1f431;作者&#xff1a;一只大喵咪1201 &#x1f431;专栏&#xff1a;《RTOS学习》 &#x1f525;格言&#xff1a;你只管努力&#xff0c;剩下的交给时间&#xff01; 目录 &#x1f353;信号量和互斥量&#x1f345;创建&#x1f345;Take&#x1f345;Give &#x…

中国激光雷达的2023:倔强的笨小孩

作者 |David 编辑 |王博 现在回头来看&#xff0c;从2007年莱万多夫斯基和大卫霍尔在硅谷骑着摩托车四处兜售激光雷达开始&#xff0c;到2023年仅中国车载市场出货量接近60万&#xff0c;覆盖了市面上40%以上搭载高阶智驾的新车型&#xff0c;激光雷达一直在用有力的数据回应着…

华为atlas300安装教程

1、安装包位置&#xff1a; /data/ai_install_packages 2、添加HwHiAiUser用户&#xff1a; groupadd -g 1000 HwHiAiUser useradd -g HwHiAiUser -u 1000 -d /home/HwHiAiUser -m HwHiAiUser -s /bin/bash 3、安装驱动&#xff1a; ./Ascend-hdk-310p-npu-driver_6.0.0_l…

HashMap扩容是2倍的原因(全网博客几乎都解释错了)

零、前言 最近在写博客时&#xff0c;突然又想起来哪个经常出现在面试题里的问题&#xff1a; HashMap扩容为什么是原来的2倍&#xff1f; 因为看过源码&#xff0c;我觉得这个问题并不难。在我之前的通俗解释equals和hashCode的关系和作用里也说过这个原因。但为了博客的严谨…

DesignDoll使用方法

选择材质球 取消网格线 控制手部动作-设置左右手 - 手部运动 控制身材 控制身高 比例
最新文章