[C/C++] -- 大数的加减法

news2024/11/15 9:30:28

大数加减法的问题主要产生于计算机基本数据类型的表示范围限制。通常情况下,计算机采用有限位数的数据类型(如int、long)来表示整数,这些数据类型的表示范围有限,无法表示超出范围的大整数。

例如超过了long类型的表示范围(通常为-9223372036854775808 到 9223372036854775807),就无法直接使用基本数据类型进行计算。这时候就需要使用特殊的方法来表示和处理这些大数。

在实际应用中,大数加减法常见于金融计算、科学计算、密码学等领域。例如,在金融领域中,需要处理非常大的金额或利率;在科学计算中,可能需要处理超过基本数据类型表示范围的测量数据或计算结果;在密码学中,需要处理大素数或大整数来进行加密、解密等操作。

  1. 大数表示方法:使用字符串或数组等数据结构来表示大数。每个位上的数字可以用字符 '0' 到 '9' 来表示,例如:"12345678901234567890"。

  2. 大数加法

    • 从两个大数的最低位开始逐位相加,将结果存储到一个新的字符串或数组中。
    • 需要考虑进位的情况,即当相加结果超过9时,需要将进位加到下一位的运算中。
    • 需要考虑两个大数长度不等的情况。
  3. 大数减法

    • 从两个大数的最低位开始逐位相减,将结果存储到一个新的字符串或数组中。
    • 需要考虑借位的情况,即当相减结果小于0时,需要向高位借位。
    • 需要考虑被减数小于减数的情况,可以在代码中进行判断并抛出异常或返回特定结果。
  4. 进位和借位处理:编写逻辑来处理加法中的进位和减法中的借位。

运算符重载函数声明为 BigInt 类的友元函数是为了在不暴露私有成员的情况下,让它们能够有效地操作 BigInt 对象。

class BigInt 
{
public:
	BigInt(string str) : strDigit(str){ }
private:
	string strDigit;//使用字符串存储大整数
	//都是全局函数声明为友元
	friend ostream& operator<<(ostream& out, const BigInt& src);
	friend BigInt operator+(const BigInt& lhs, const BigInt& rhs);
	friend BigInt operator-(const BigInt& lhs, const BigInt& rhs);

};

 重载输出流插入运算符 <<,允许使用 << 将 BigInt 对象输出到输出流中。ostream& out 是一个输出流对象的引用,它表示要输出的目标流,而 const BigInt& src 是一个常量引用,表示要输出的 BigInt 对象。

ostream& operator<<(ostream& out, const BigInt& src)
{
	out << src.strDigit;
	return out;
}

 完整代码:

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

class BigInt 
{
public:
	BigInt(string str) : strDigit(str){ }
private:
	string strDigit;//使用字符串存储大整数
	//都是全局函数声明为友元
	friend ostream& operator<<(ostream& out, const BigInt& src);
	friend BigInt operator+(const BigInt& lhs, const BigInt& rhs);
	friend BigInt operator-(const BigInt& lhs, const BigInt& rhs);

};
//打印函数
ostream& operator<<(ostream& out, const BigInt& src)
{
	out << src.strDigit;
	return out;
}

//大数加法
BigInt operator+(const BigInt& lhs, const BigInt& rhs) {
	//从后往前遍历字符串lhs,和rhs,用flag来标记是否需要进位,结果存在result数组中(反向的)
	string result;
	bool flag = false;
	int i = lhs.strDigit.length() - 1;//string的length和size没有区别,length是沿用C语言习惯,早先只有length
	int j = rhs.strDigit.length() - 1;

	for (; i >= 0 && j >= 0; --i, --j) //i 和 j其中一个会被减到负数然后退出for循环
	{
		int ret = lhs.strDigit[i] - '0' + rhs.strDigit[j] - '0';//单个位加减时需要减'0'转成整型,每次都重新定义一个新的ret
		if (flag)
		{
			ret += 1;//flag为true说明此位因为上一位进位而需要多加一个1
			flag = false;//再将其重新置为false
		}
		//两个if不能互换,否则不是下一位进1
		if (ret >= 10)
		{
			ret %= 10;
			flag = true;
		}
		result.push_back(ret + '0');
	}
	//如果遍历完,i还有剩下,第一个字符串没完
	if (i >= 0)//注意要取等,因为更短的那个字符串的下标是被减到-1,而不是0,0依然说明还剩余1位
	{
		while (i >= 0)
		{
			int ret = lhs.strDigit[i] - '0';
			if (flag)                 //前面加过来可能还有进位,然后当前可能为9,加了1之后又 = 10,又得进位,所以直接复制前面的代码
			{
				ret += 1;
				flag = false;
			}
			if (ret >= 10)
			{
				ret %= 10;
				flag = true;
			}
			result.push_back(ret + '0');
			i--;	
		}
	}
	//第二个字符串没完
	else if (j >= 0)
	{
		while (j >= 0)
		{
			int ret = lhs.strDigit[j] - '0';
			if (flag)                 //前面加过来可能还有进位,然后当前可能为9,加了1之后又 = 10,又得进位,所以直接复制前面的代码
			{
				ret += 1;
				flag = false;
			}
			if (ret >= 10)
			{
				ret %= 10;
				flag = true;
			}
			result.push_back(ret + '0');
			j--;
			
		}
	}
	//最高位可能也进位
	if (flag) {
		result.push_back('1');
	}

	reverse(result.begin(), result.end());
	
	return result;//result是string类,而不是BigInt,因为隐式转换,p263
	//因为编译器看到该类的构造函数只接受一个实参(而且是string类的),所以可以触发隐式转换机制,定义string result时编译器也会构建一个BigInt类的临时对象,并把result赋值给他
	//函数里的return本来就是返回临时对象,这个时候就返回的就是那个BigInt类的临时对象
	//当然,参考书上的例子,即使后面函数用string类的result,编译器实际传入的也是BigInt的临时对象
	//如某成员函数定义为func(BigInt& a){...}; 调用时传入string类 func(result);也是合法的
}

//大数减法
BigInt operator-(const BigInt& lhs, const BigInt& rhs) {
	//让大的减小的,如果lhs比rhs小,则让rhs - lhs,然后最后添加负号
	string result;
	bool flag = false;
	bool minor = false;//标记lhs是否和rhs互换了

	string maxStr = lhs.strDigit;
	string minStr = rhs.strDigit;
	if (maxStr.length() < minStr.length())
	{
		//互换,让maxStr一直是最长的
		maxStr = rhs.strDigit;
		minStr = lhs.strDigit;
		minor = true;
	}
	//长度一样也得比较
	else if (maxStr.length() == minStr.length())
	{
		if (maxStr < minStr)
		{
			maxStr = rhs.strDigit;//让maxStr是最大的
			minStr = lhs.strDigit;
			minor = true;
		}
		else if (maxStr == minStr)
		{
			return string("0");
		}
	}

	int i = maxStr.length() - 1;//i肯定大于等于j,所以后面j会先完
	int j = minStr.length() - 1;

	for (; i >= 0 && j >= 0; --i, --j) {
		int ret = maxStr[i]  - minStr[j];//减法的话,char类型相减就是int型了,不用+‘0’再相减
		/*if (ret >= 0)
		{
			result.push_back(ret + '0');
		} 一定要先看标记,因为被借位的话,当前ret需要减1*/
		if (flag)
		{
			ret -= 1;
			flag = false;
		}
		//当前位有可能因为被借位了而减,小于0,所以紧接着判断是否为负
		if (ret < 0)
		{
			ret += 10;// 如2 - 6,应该是12 - 6,所以为 2 - 6 + 10 = 6
			flag = true;
		}
		result.push_back(ret + '0');
	}

	//肯定是j先完,所以不用再像加法那样判断,而是直接把i多余的处理完
	while (i >= 0) {
		int ret = maxStr[i] - '0';
		if (flag) {
			ret -= 1;
			flag = false;
		}
		//同样的,ret可能原本是0,被借位了又为-1了
		if (ret < 0) {
			ret += 10;
			flag = true;
		}
		result.push_back(ret + '0');
		i--;
	}
	//翻转前先看看末尾有没有0,如1000,否则反转后就是0001
	while(result.back() == '0')
	{
		result.pop_back();
	}
	

	if (minor) {
		result.push_back('-');
	}
	reverse(result.begin(), result.end());
	return result;
}

int main()
{
	string s1;
	string A;
	string s2;
	getline(cin, s1);
	getline(cin, A);//把加减号定义为char会报错,getline第二个参数只能是string类型
	getline(cin, s2);
	BigInt int1(s1);
	BigInt int2(s2);
	if (A == "+") {
		cout << int1 + int2 << endl;
	}
	if (A == "-") {
		cout << int1 - int2 << endl;
	}
	return 0;
}

 

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

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

相关文章

支持播放h265的插件

插件源码地址&#xff1a;GitCode - 开发者的代码家园https://gitcode.com/mirrors/nanguantong/flv-h265.js/overview 1. 下载代码 运行以下命令 npm i npm run build npm run build 后生成 flv.min.js 文件&#xff0c;引入使用 2. 调用 js 文件&#xff1a; let flvj…

【数据结构】链式栈

链式栈 一、链式栈的栈顶在哪里&#xff1f; 二、链式栈的结构&#xff1a; typedef struct LSNode {int data;struct LSNode* next; }LSNode, *PLStack; // 链栈的节点&#xff0c;由于栈顶在第一个数据节点&#xff0c;所以不需要top指针三、链式栈的实现 //初始化 void …

价格中间加横杠

<span></span>59 <span class"price">177</span>.price{ text-decoration:line-through; }

vue2和vue3区别: 探索关键差异

vue2和vue3区别&#xff1a; 探索关键差异 Vue.js 作为流行的前端框架&#xff0c;其版本 3 带来了许多令人兴奋的改进和新功能。虽然 Vue 3 保持了与 Vue 2 的相似性&#xff0c;但也存在一些关键差异需要开发者注意。本文将通过表格形式&#xff0c;清晰地展现 Vue 2 和 Vue …

清理缓存简单功能实现

在程序开发中&#xff0c;经常会用到缓存&#xff0c;最常用的后端缓存技术有Redis、MongoDB、Memcache等。 而有时候我们希望能够手动清理缓存&#xff0c;点一下按钮就把当前Redis的缓存和前端缓存都清空。 功能非常简单&#xff0c;创建一个控制器类CacheController&#xf…

Android Studio连接MySQL8.0

【序言】 移动平台这个课程要做一个app的课设&#xff0c;我打算后期增加功能改成毕设&#xff0c;就想要使用MySQL来作为数据库&#xff0c;相对于SQLlite来说&#xff0c;我更熟悉MySQL一点。 【遇到的问题】 一直无法连接上数据库&#xff0c;开始的时候查了很多资料&#…

Jmeter中线程组介绍

1.线程数的意义 Jmeter采用了线程来模拟用户&#xff0c;即1个线程代表1个用户&#xff0c;线程可以简单理解为计算机处理任务时的一个具体执行人。 一个任务可以由多个人&#xff08;线程&#xff09;共同完成&#xff0c;也可以由一个人&#xff08;线程&#xff09;来完成&a…

基于LLM的自行车道CAD

LLM&#xff08;大型语言模型&#xff09;是强大的工具。对于许多人来说&#xff0c;用语言表达愿望通常比浏览复杂的 GUI 更简单。 1、系统简介和环境搭建 urb-x.ch&#xff0c;这是一家专门从事自行车道建设的公司。轨道采用模块化构建块进行独特设计&#xff0c;可以通过多…

AI工具的热门与卓越:揭示AI技术的实际应用和影响

文章目录 每日一句正能量前言常用AI工具创新AI应用个人体验分享后记 每日一句正能量 我们在我们的劳动过程中学习思考&#xff0c;劳动的结果&#xff0c;我们认识了世界的奥妙&#xff0c;于是我们就真正来改变生活了。 前言 随着人工智能&#xff08;AI&#xff09;技术的快…

prometheus、mysqld_exporter、node_export、Grafana安装配置

工具简介 Prometheus&#xff08;普罗米修斯&#xff09;&#xff1a;是一个开源的服务监控系统和时间序列数据库 mysqld_exporter&#xff1a; 用于监控 mysql 服务器的开源工具&#xff0c;它是由 Prometheus 社区维护的一个官方 Exporter。该工具通过连接到mysql 服务器并执…

每日一题9:Pandas-填充缺失值

一、每日一题 DataFrame products --------------------- | Column Name | Type | --------------------- | name | object | | quantity | int | | price | int | ---------------------编写一个解决方案&#xff0c;在 quantity 列中将缺失的值 编…

flutter开发实战-人脸识别相机使用

flutter开发实战-人脸识别相机使用 当需要拍摄的时候&#xff0c;需要检测到人脸再进行后续的操作&#xff0c;这里使用的是face_camera 一、引入face_camera 在工程的pubspec.yaml中引入插件 # 检测人脸face_camera: ^0.0.8iOS端需要设置相关权限 在info.plist文件中&…

编程代码的舞者--Python循环语句

循环语句是编程中不可或缺的要素之一&#xff0c;它们能够让程序反复执行特定的任务&#xff0c;提高代码的重复利用性和效率。在本篇博客中&#xff0c;我们将深入探讨Python中常用的循环语句&#xff0c;包括for循环和while循环&#xff0c;以及控制循环流程的关键字break和c…

【Win10设备管理器中无端口选项】

计算机疑难杂症分享002 Win10设备管理器中无端口选项1、问题现象2、问题原因3、问题解决3.1、驱动精灵(亲测的此方法)3.2、添加过时硬件3.3、官方的方法 Win10设备管理器中无端口选项 1、问题现象 当我调试串口通信时&#xff0c;发现打开设备管理器没有端口&#xff0c;打开…

系统需求开发和管理指南(软件标准文件Word)

1.需求获取的方式 2.需求分析的准则 3.需求分析的方法 4.需求开发考虑的方面 5.需求确认的方法 6.需求优先级的设定 7.需求文档编制规范要求 软件全文档获取方式一&#xff1a;本文末个人名片直接获取。 软件全文档获取二&#xff1a;软件项目开发全套文档下载_软件项目文档-C…

Unity开发中导弹路径散射的原理与实现

Unity开发中导弹路径散射的原理与实现 前言逻辑原理代码实现导弹自身脚本外部控制脚本 应用效果结语 前言 前面我们学习了导弹的追踪的效果&#xff0c;但是在动画或游戏中&#xff0c;我们经常可以看到导弹发射后的弹道是不规则的&#xff0c;扭扭曲曲的飞行&#xff0c;然后击…

Leetcode39.组合总和

文章目录 题目描述解题思路重复子集剪枝 代码 题目 参考题解 题目描述 给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target &#xff0c;找出 candidates 中可以使数字和为目标数 target 的 所有 不同组合 &#xff0c;并以列表形式返回。你可以按 任意顺序 返…

Python | Leetcode Python题解之第78题子集

题目&#xff1a; 题解&#xff1a; class Solution:def subsets(self, nums: List[int]) -> List[List[int]]:self.res []self.backtrack([], 0, nums)return self.resdef backtrack(self, sol, index, nums):self.res.append(sol)for i in range(index, len(nums)):self…

leetcode-矩阵最长递增路径-102

题目要求 思路 1.通过双循环去把每一个结点作为起始点进行统计&#xff0c;将返回的路径长度存放在res中&#xff0c;取最大的res的长度。 2.递归中需要的几个值&#xff0c;x和y当前结点的坐标&#xff0c;pre用于存储上一个结点的元素值&#xff0c;因为要求是路径上的元素是…

Redis简单使用

认识Redis redis&#xff1a;字典型数据库&#xff0c;存储的是键值对&#xff0c;是NoSql数据库 关系型数据库和NoSql之间的区别&#xff1a; 结构化&#xff1a;NoSql非结构化数据库&#xff0c;松散结构&#xff08;键值对key-value&#xff08;可以任意类型&#xff09;&…