C++ 整型大数运算(大整数运算)项目

news2024/11/26 11:11:11

C++ 整型大数运算项目

  • 一、项目介绍
  • 二、项目变量成员
  • 三、项目实现
    • 构造函数
    • 加法
    • 减法
    • 乘法
      • 先计算再进位
      • 边计算边进位
    • 除法与取模
    • 判断
    • 输入输出
  • 四、项目源代码展示
    • 在 Big_integer.h 中:
    • 在 Big_integer.cpp 中:
  • 五、测试准确性
  • 六、优化方向

一、项目介绍

整型大数运算,也就是大整数运算,是为了计算数据大小超过 long long 类型表示范围 [ -2 ^ 63, 2 ^ 63 - 1 ] 的整型数据。

整个项目最核心的目标是实现 四则运算 与 取模,外加 输入输出 与 两个大整数的大小判断

这里实现的四则运算借用的是小学时候使用的竖式计算,将思想转化为实际的代码。

以下代码环境为 VS 2022 C++。

二、项目变量成员

namespace my
{
	class Big_integer
	{
	private:

		enum Big_integer_sign				// 使用枚举体,防止赋值错误
		{
			positive = '+',
			negative = '-',
		};

		std::string _nums;					// 使用 string 存储具体数据
		Big_integer_sign _sign;				// 将符号单独列出,便于判断
	};
}
  1. 存储数字的容器使用 string ,方便其他数据转换为 string 再转为 Big_integer 。

  2. 将符号单独列出是因为用 string 存储符号会导致后面运算的附加判断很麻烦。

  3. 使用枚举体,防止赋值其他符号出现错误。

三、项目实现

构造函数

因为使用的是 string 存储数据,我们可以使用 to_string 函数将内置类型全部转换为 string 类型,不过要对 string 中的符号判断,例如出现 “-10” 这种数字前出现负号的 string 需要设计函数将符号与数字分离。

		Big_integer(const std::string& str = "0")
			:_sign(signExpressSolve(str))
			,_nums(str, signLastIndex(str) + 1)
		{
			;
		}

		Big_integer(const Big_integer& number)
		{
			_nums = number._nums;
			_sign = number._sign;
		}

		Big_integer(const long double number)
		{
			*this = Big_integer(std::to_string((long long)number));
		}

		Big_integer(const double number)
		{
			*this = Big_integer(std::to_string((long long)number));
		}

		Big_integer(const float number)
		{
			*this = Big_integer(std::to_string((int)number));
		}

		Big_integer(const unsigned long long number)
		{
			*this = Big_integer(std::to_string(number));
		}

		Big_integer(const long long number)
		{
			*this = Big_integer(std::to_string(number));
		}

		Big_integer(const unsigned long number)
		{
			*this = Big_integer(std::to_string(number));
		}

		Big_integer(const long number)
		{
			*this = Big_integer(std::to_string(number));
		}

		Big_integer(const unsigned int number)
		{
			*this = Big_integer(std::to_string(number));
		}

		Big_integer(const int number)
		{
			*this = Big_integer(std::to_string(number));
		}

		Big_integer(const unsigned short number)
		{
			*this = Big_integer(std::to_string(number));
		}

		Big_integer(const short number)
		{
			*this = Big_integer(std::to_string(number));
		}

		Big_integer(const char* str)
		{
			*this = Big_integer(std::string(str));
		}
		static Big_integer_sign signExpressSolve(const std::string& str)
		{
			signed char sign = 1;
			for (auto e : str)
			{
				if (e == negative)
				{
					sign = -sign;
				}
				else if (e == positive)
				{
					;
				}
				else
				{
					break;
				}
			}
			return sign == 1 ? positive : negative;
		}

		static size_t signLastIndex(const std::string& str)
		{
			size_t index = -1;
			for (auto e : str)
			{
				if (e == negative || e == positive)
				{
					++index;
				}
				else
				{
					break;
				}
			}
			return index;
		}

加法

加法计算时只需模拟进位即可:
在这里插入图片描述
不过要注意符号,如下图:

正数 + 正数 和 负数 + 负数 在加法中不受影响,但是 正数 + 负数 和 负数 + 正数 需要转为减法,这意味其中一个数要 变号
在这里插入图片描述
这里先对 += 进行重载,因为返回的是 引用 ,会少一次拷贝

每位数计算后应该倒序存储,因为 string 空间是连续的,头插消耗大:

	Big_integer& Big_integer::operator+=(const Big_integer& number)
	{
		if (_sign == positive && number._sign == negative)	// 正数 + 负数
		{										// number 变号使用减法,再变回来
			number.signChange();
			*this -= number;
			number.signChange();

			return *this;
		}
		if (_sign == negative && number._sign == positive)	// 负数 + 正数
		{										// this 变号使用减法,再变回来
			signChange();
			*this -= number;
			signChange();

			return *this;
		}

		int len1 =		  _nums.size() - 1;
		int len2 = number._nums.size() - 1;
		int next = 0;							// 高位存储

		std::string copy;
		copy.reserve((len1 > len2 ? len1 : len2) + 5);

		while (len1 >= 0 || len2 >= 0 || next > 0)
		{
			int num1 = len1 >= 0 ?		  _nums[len1--] - '0' : 0;
			int num2 = len2 >= 0 ? number._nums[len2--] - '0' : 0;
			int ret = num1 + num2 + next;		// 当前位数计算

			next = ret / 10;					// 进高位
			ret = ret % 10;						// 保留低位

			copy += (ret + '0');				// 存进结果
		}

		reverse(copy.begin(), copy.end());		// 翻转
		_nums = std::move(copy);				// 获取结果
		return *this;
	}
		void signChange() const
		{
			int* change = (int*)(&_sign);
			*change = (_sign == positive ? negative : positive);
		}

另外,与 内置类型 相加时会将 内置类型 隐式类型转换为 Big_integer,不用再写其他转换的函数。

接下来关于加法相关的函数都可以复用 += 运算符:

  1. + 重载运算符直接放在全局,等价于 大数 + 内置类型内置类型 + 大数

  2. 后置 和 前置 ++ 复用 += 即可。

		Big_integer operator++(int)				// 后置 ++
		{
			Big_integer temp = *this;
			++(*this);

			return temp;
		}

		Big_integer& operator++()				// 前置 ++
		{
			return *this += 1;
		}

		Big_integer operator+() const		// +数 等于一个表达式,这里只是闲的事不多加上的
		{
			return *this;
		}

	Big_integer operator+(const Big_integer& number1, const Big_integer& number2)
	{												// 为方便使用,将 + 放在全局
		return Big_integer(number1) += number2;	
	}

减法

使用减法模拟时要注意谁的绝对值最大谁在前,保证 大 - 小,不然会出错:
在这里插入图片描述
同加法一样要注意符号,如下图:

正数 - 正数 和 负数 - 负数 在减法中不受影响,但是 正数 - 负数 和 负数 - 正数 需要转为加法,这意味其中一个数要 变号 。
在这里插入图片描述
注意,上面视图中的 9 - 12 是要将绝对值大的当 被减数,小的当 减数,意味着要额外判断:

	Big_integer& Big_integer::operator-=(const Big_integer& number)
	{
		if (_sign == positive && number._sign == negative)
		{									
			number.signChange();
			*this += number;
			number.signChange();

			return *this;
		}
		if (_sign == negative && number._sign == positive)
		{
			signChange();
			*this += number;
			signChange();

			return *this;
		}

		int change = 1;						// this 是否变号
		int compare = justThanSize(number);	// 绝对值比较
		if (compare < 0)
		{
			change = -1;					// this 的值小于 number,则 this 需要变号
		}
		else if (compare == 0)				// 特判解决当相减为零时出现负号情况,如:-3 + 3 = -0
		{
			return *this = 0;
		}

		int len1 =		  _nums.size() - 1;
		int len2 = number._nums.size() - 1;
		int next = 0;						// 借位

		std::string copy;
		copy.reserve((len1 > len2 ? len1 : len2) + 5);

		while (len1 >= 0 || len2 >= 0)
		{
			int num1 = len1 >= 0 ?		  _nums[len1--] - '0' : 0;
			int num2 = len2 >= 0 ? number._nums[len2--] - '0' : 0;
			int ret = change * (num1 - num2) + next;

			if (ret < 0)					// 当前位为负数,向上借 1
			{
				next = -1;					// 上一位被借 1,需要 -1
				ret += 10;					// 上一位借的 1 ,在当前位为 10 
			}
			else							// 不为负数则不用借
			{
				next = 0;
			}

			copy += (ret + '0');
		}

		//最高位去零
		while (copy.size() > 1 && copy[copy.size() - 1] == '0')
		{
			copy.pop_back();
		}
		if (change == -1)					// 变号
		{
			signChange();
		}

		reverse(copy.begin(), copy.end());
		_nums = std::move(copy);
		return *this;
	}
		int justThanSize(const Big_integer& number) const
		{
			int len1 = (int)_nums.size() - 1;
			int len2 = (int)number._nums.size() - 1;

			if (len1 != len2)
			{
				return len1 - len2;
			}

			for (int i = 0; i <= len1; ++i)
			{
				if (_nums[i] != number._nums[i])
				{
					return _nums[i] - number._nums[i];
				}
			}

			return 0;
		}

接下来处理于减法相关函数:

		Big_integer operator--(int)
		{
			Big_integer temp = *this;
			--(*this);
			return temp;
		}

		Big_integer& operator--()
		{
			return *this -= 1;
		}

		Big_integer operator-() const		// -数 表示变号
		{
			Big_integer temp = *this;
			temp.signChange();
			return temp;
		}
		
	// 同理于 + 运算符函数
	Big_integer operator-(const Big_integer& number1, const Big_integer& number2)
	{
		return Big_integer(number1) -= number2;
	}

乘法

乘法竖式计算有两种,第一种先将各位数结果保存,后进位。第二种是边乘边进位:
在这里插入图片描述
另外,对于结果存储位数大小是根据两个相乘的数的位数个数决定的:
在这里插入图片描述

所以两个不等于 0 的整数,其中一个位数为 x,另一个为 y,两者相乘时位数是相加的,则结果的位数 z 范围为 [ x + y - 1,x + y]。

这里为了保证计算,位数空间开到 x + y ,若位数数量为 x + y - 1,只需一次最高位去零即可。

对于符号的处理,因为先实现的是 *= 运算符函数,需要注意对 this 变号:
在这里插入图片描述

先计算再进位

	Big_integer& Big_integer::operator*=(const Big_integer& number)
	{
		if (*this == 0 || number == 0)	// 有 0 时直接返回 0
		{								// 注意这里还没有实现大数的判断
			return *this = 0;
		}

		// 符号问题只需关注*this
		if (_sign == positive && number._sign == negative)
		{
			_sign = negative;
		}
		else if (_sign == negative && number._sign == negative)
		{
			_sign = positive;
		}

		int len1 =		  _nums.size() - 1;
		int len2 = number._nums.size() - 1;

		int maxN = (len1 + 1) + (len2 + 1);
		int index = maxN - 1;
		std::vector<int> copy(maxN, 0);			// signed char 存储会有溢出风险

		// 乘法
		while (len1 >= 0)						// 先将相乘结果存储
		{
			int i = maxN - index - 1;
			int num1 = len1 >= 0 ? _nums[len1] - '0' : 0;

			for (int line = len2; line >= 0; --line)
			{
				int num2 = line >= 0 ? number._nums[line] - '0' : 0;
				
				copy[i] += num1 * num2;
				++i;
			}

			--index;
			--len1;
		}

		for (int i = 0; i < copy.size(); ++i)	// 后进位
		{
			if (copy[i] > 9)
			{
				copy[i + 1] += copy[i] / 10;
				copy[i] %= 10;
			}
			copy[i] += '0';
		}

		// 最高位去零
		if (copy[copy.size() - 1] == '0')
		{
			copy.pop_back();
		}
		reverse(copy.begin(), copy.end());
		_nums = std::string(copy.begin(), copy.end());
		return *this;
	}

边计算边进位

	Big_integer& Big_integer::operator*=(const Big_integer& number)
	{
		//if (*this == 0 || number == 0)	// 有 0 时直接返回 0
		//{
		//	return *this = 0;
		//}

		// 符号问题只需关注*this
		if (_sign == positive && number._sign == negative)
		{
			_sign = negative;
		}
		else if (_sign == negative && number._sign == negative)
		{
			_sign = positive;
		}

		int len1 =		  _nums.size() - 1;
		int len2 = number._nums.size() - 1;

		int maxN = (len1 + 1) + (len2 + 1);
		int index = maxN - 1;
		std::string copy(maxN, '0');

		// 乘法
		while (len1 >= 0)
		{
			int next = 0;
			int i = maxN - index - 1;
			int num1 = len1 >= 0 ? _nums[len1] - '0' : 0;

			for (int line = len2; line >= 0; --line)
			{
				int num2 = line >= 0 ? number._nums[line] - '0' : 0;
				int ret = num1 * num2 + next;

				next = ret / 10;
				ret = ret % 10;

				next +=   ((copy[i] - '0') + ret) / 10;
				copy[i] = ((copy[i] - '0') + ret) % 10 + '0';
				++i;
			}
			if (next > 0)
			{
				copy[i] += next;
			}
			--index;
			--len1;
		}

		// 最高位去零
		if (copy[copy.size() - 1] == '0')
		{
			copy.pop_back();
		}
		reverse(copy.begin(), copy.end());
		_nums = std::move(copy);
		return *this;
	}

接下来处理 * 号:

	Big_integer operator*(const Big_integer& number1, const Big_integer& number2)
	{
		return Big_integer(number1) += number2;
	}

除法与取模

除法与取模计算效率比较低下:
在这里插入图片描述
注意试乘方法从 0 到 9 ,可用二分进行小优化,要注意借用乘法、加法、减法时会将数字变号,所以将符号留到最后再判断:

	Big_integer& Big_integer::operator/=(const Big_integer& number)
	{
		// 除数不能为零
		assert(number != 0);

		// 除数大于被除数直接为零
		if (justThanSize(number) < 0)
		{
			return *this = 0;
		}

		Big_integer_sign sign1 = _sign;
		Big_integer_sign sign2 = number._sign;

		int len1 =		  _nums.size() - 1;
		int len2 = number._nums.size() - 1;
		int condition = len1 - len2;		// 对齐
		int count = 0;

		std::string copy;
		copy.reserve(len1);

		while (len1 - len2 - count >= 0)
		{
			int left = -1;
			int right = 10;
			while (left + 1 < right)		// 二分试乘
			{
				int mid = left + (right - left) / 2;
				if (justThanSize(number * mid * _pow(10, condition)) >= 0)
				{
					left = mid;
				}
				else
				{
					right = mid;
				}
			}
			if (left != 0)					// 结果减(加)去试乘的数
			{
				if (_sign == number._sign)
				{
					*this -= (left * number) * _pow(10, condition);
				}
				else
				{
					*this += (left * number) * _pow(10, condition);
				}
			}

			copy += (left + '0');
			++count;
			--condition;
		}

		// 除后符号放后面,防止加减乘过程中为零负号变为正号
		if (sign1 == positive && sign2 == negative)
		{
			_sign = negative;
		}
		else if (sign1 == negative && sign2 == negative)
		{
			_sign = positive;
		}
		else
		{
			_sign = sign1;
		}

		if (copy[0] == '0')					// 最高位去零
		{
			copy.erase(copy.begin());
		}
		_nums = std::move(copy);
		return *this;
	}

_pow 函数是位数对齐防止溢出,取模运算直接复用除乘减法即可:

	Big_integer Big_integer::_pow(const Big_integer& number, const int& index)
	{
		if (index == 0)
		{
			return 1;
		}

		Big_integer temp = _pow(number, index / 2);
		if (index % 2 == 0)
		{
			return temp * temp;
		}
		else
		{
			return temp * temp * number;
		}
	}
	
	Big_integer& Big_integer::operator%=(const Big_integer& number)
	{
		Big_integer temp = *this;
		temp /= number;

		return *this -= temp * number;
	}

	Big_integer operator/(const Big_integer& number1, const Big_integer& number2)
	{
		return Big_integer(number1) /= number2;
	}

	Big_integer operator%(const Big_integer& number1, const Big_integer& number2)
	{
		return Big_integer(number1) %= number2;
	}

判断

判断只需写好 == 与 < ,其他复用两者:

	bool operator!(const Big_integer& number)
	{
		return number == 0;
	}

	bool operator!=(const Big_integer& number1, const Big_integer& number2)
	{
		return !(number1 == number2);
	}

	bool operator>=(const Big_integer& number1, const Big_integer& number2)
	{
		return !(number1 < number2);
	}

	bool operator> (const Big_integer& number1, const Big_integer& number2)
	{
		return !(number1 <= number2);
	}

	bool operator<=(const Big_integer& number1, const Big_integer& number2)
	{
		return number1 < number2 || number1 == number2;
	}

	bool operator==(const Big_integer& number1, const Big_integer& number2)
	{
		if (number1._sign == number2._sign && number1._nums == number2._nums)
		{
			return true;
		}
		else
		{
			return false;
		}
	}

	bool operator< (const Big_integer& number1, const Big_integer& number2)
	{
		if (number1._sign == Big_integer::negative && number2._sign == Big_integer::positive)
		{
			return true;
		}
		if (number1._sign == Big_integer::positive && number2._sign == Big_integer::negative)
		{
			return false;
		}

		int lessNum = number1.justThanSize(number2);
		if (lessNum < 0)
		{
			return number1._sign == Big_integer::positive ? true : false;
		}
		if (lessNum > 0)
		{
			return number1._sign == Big_integer::positive ? false : true;
		}
		return false;
	}

输入输出

	std::ostream& my::operator<<(std::ostream& out, const my::Big_integer& number)
	{
		if (number._sign == my::Big_integer::negative)
		{
			out << '-';
		}
		out << number._nums;
		return out;
	}

	std::istream& my::operator>>(std::istream& in, my::Big_integer& number)
	{
		std::string temp;
		in >> temp;
		number = temp;
		return in;
	}

四、项目源代码展示

在 Big_integer.h 中:

#pragma once 

#include <iostream>
#include <string>

namespace my
{
	class Big_integer
	{
	private:

		enum Big_integer_sign				// 使用枚举体,防止赋值错误
		{
			positive = '+',
			negative = '-',
		};

		std::string _nums;					// 使用 string 存储具体数据
		Big_integer_sign _sign;				// 将符号单独列出,便于判断

	public:			// 构造函数

		Big_integer(const std::string& str = "0")
			:_sign(signExpressSolve(str))
			,_nums(str, signLastIndex(str) + 1)
		{
			;
		}

		Big_integer(const Big_integer& number)
		{
			_nums = number._nums;
			_sign = number._sign;
		}

		Big_integer(const long double number)
		{
			*this = Big_integer(std::to_string((long long)number));
		}

		Big_integer(const double number)
		{
			*this = Big_integer(std::to_string((long long)number));
		}

		Big_integer(const float number)
		{
			*this = Big_integer(std::to_string((int)number));
		}

		Big_integer(const unsigned long long number)
		{
			*this = Big_integer(std::to_string(number));
		}

		Big_integer(const long long number)
		{
			*this = Big_integer(std::to_string(number));
		}

		Big_integer(const unsigned long number)
		{
			*this = Big_integer(std::to_string(number));
		}

		Big_integer(const long number)
		{
			*this = Big_integer(std::to_string(number));
		}

		Big_integer(const unsigned int number)
		{
			*this = Big_integer(std::to_string(number));
		}

		Big_integer(const int number)
		{
			*this = Big_integer(std::to_string(number));
		}

		Big_integer(const unsigned short number)
		{
			*this = Big_integer(std::to_string(number));
		}

		Big_integer(const short number)
		{
			*this = Big_integer(std::to_string(number));
		}

		Big_integer(const char* str)
		{
			*this = Big_integer(std::string(str));
		}

	public:			// 加法

		Big_integer operator++(int)				// 后置 ++
		{
			Big_integer temp = *this;
			++(*this);

			return temp;
		}

		Big_integer& operator++()				// 前置 ++
		{
			return *this += 1;
		}

		Big_integer operator+() const		// +数 等于一个表达式,这里只是闲的事不多加上的
		{
			return *this;
		}

		Big_integer& operator+= (const Big_integer& number);

	public:			// 减法

		Big_integer operator--(int)
		{
			Big_integer temp = *this;
			--(*this);
			return temp;
		}

		Big_integer& operator--()
		{
			return *this -= 1;
		}

		Big_integer operator-() const		// -数 表示变号
		{
			Big_integer temp = *this;
			temp.signChange();
			return temp;
		}

		Big_integer& operator-=(const Big_integer& number);

	public:

		Big_integer& operator*=(const Big_integer& number);

		Big_integer& operator/=(const Big_integer& number);

		Big_integer& operator%=(const Big_integer& number);

		friend std::ostream& operator<<(std::ostream& out, const Big_integer& number);

		friend std::istream& operator>>(std::istream& in, Big_integer& number);

	public:		// 判断

		friend bool operator==(const Big_integer& number1, const Big_integer& number2);

		friend bool operator< (const Big_integer& number1, const Big_integer& number2);

	public:

		void swap(Big_integer& number)
		{
			std::swap(_nums, number._nums);
			std::swap(_sign, number._sign);
		}

	private:

		static Big_integer _pow(const Big_integer& number, const int& index);

		void signChange() const
		{
			int* change = (int*)(&_sign);
			*change = (_sign == positive ? negative : positive);
		}

		int justThanSize(const Big_integer& number) const;

		static Big_integer_sign signExpressSolve(const std::string& str);

		static size_t signLastIndex(const std::string& str);
	};

	bool operator!(const Big_integer& number);

	bool operator!=(const Big_integer& number1, const Big_integer& number2);

	bool operator>=(const Big_integer& number1, const Big_integer& number2);

	bool operator> (const Big_integer& number1, const Big_integer& number2);

	bool operator<=(const Big_integer& number1, const Big_integer& number2);

	Big_integer operator+(const Big_integer& number1, const Big_integer& number2);

	Big_integer operator-(const Big_integer& number1, const Big_integer& number2);

	Big_integer operator*(const Big_integer& number1, const Big_integer& number2);

	Big_integer operator/(const Big_integer& number1, const Big_integer& number2);

	Big_integer operator%(const Big_integer& number1, const Big_integer& number2);

	typedef Big_integer bint;		
}

在 Big_integer.cpp 中:

#include "Big_integer.h"
#include <cassert>

namespace my
{
	int Big_integer::justThanSize(const Big_integer& number) const
	{
		int len1 = (int)_nums.size() - 1;
		int len2 = (int)number._nums.size() - 1;

		if (len1 != len2)
		{
			return len1 - len2;
		}

		for (int i = 0; i <= len1; ++i)
		{
			if (_nums[i] != number._nums[i])
			{
				return _nums[i] - number._nums[i];
			}
		}

		return 0;
	}

	Big_integer::Big_integer_sign Big_integer::signExpressSolve(const std::string& str)
	{
		signed char sign = 1;
		for (auto e : str)
		{
			if (e == negative)
			{
				sign = -sign;
			}
			else if (e == positive)
			{
				;
			}
			else
			{
				break;
			}
		}
		return sign == 1 ? positive : negative;
	}

	size_t Big_integer::signLastIndex(const std::string& str)
	{
		size_t index = -1;
		for (auto e : str)
		{
			if (e == negative || e == positive)
			{
				++index;
			}
			else
			{
				break;
			}
		}
		return index;
	}

	Big_integer Big_integer::_pow(const Big_integer& number, const int& index)
	{
		if (index == 0)
		{
			return 1;
		}

		Big_integer temp = _pow(number, index / 2);
		if (index % 2 == 0)
		{
			return temp * temp;
		}
		else
		{
			return (temp * temp) * number;
		}
	}

	Big_integer& Big_integer::operator+=(const Big_integer& number)
	{
		if (_sign == positive && number._sign == negative)	// 正数 + 负数
		{										// number 变号使用减法,再变回来
			number.signChange();
			*this -= number;
			number.signChange();

			return *this;
		}
		if (_sign == negative && number._sign == positive)	// 负数 + 正数
		{										// this 变号使用减法,再变回来
			signChange();
			*this -= number;
			signChange();

			return *this;
		}

		int len1 =		  _nums.size() - 1;
		int len2 = number._nums.size() - 1;
		int next = 0;							// 高位存储

		std::string copy;
		copy.reserve((len1 > len2 ? len1 : len2) + 5);

		while (len1 >= 0 || len2 >= 0 || next > 0)
		{
			int num1 = len1 >= 0 ?		  _nums[len1--] - '0' : 0;
			int num2 = len2 >= 0 ? number._nums[len2--] - '0' : 0;
			int ret = num1 + num2 + next;		// 当前位数计算

			next = ret / 10;					// 进高位
			ret = ret % 10;						// 保留低位

			copy += (ret + '0');				// 存进结果
		}

		reverse(copy.begin(), copy.end());		// 翻转
		_nums = std::move(copy);				// 获取结果
		return *this;
	}

	Big_integer& Big_integer::operator-=(const Big_integer& number)
	{
		if (_sign == positive && number._sign == negative)
		{									
			number.signChange();
			*this += number;
			number.signChange();

			return *this;
		}
		if (_sign == negative && number._sign == positive)
		{
			signChange();
			*this += number;
			signChange();

			return *this;
		}

		int change = 1;						// this 是否变号
		int compare = justThanSize(number);	// 绝对值比较
		if (compare < 0)
		{
			change = -1;					// this 的值小于 number,则 this 需要变号
		}
		else if (compare == 0)				// 特判解决当相减为零时出现负号情况,如:-3 + 3 = -0
		{
			return *this = 0;
		}

		int len1 =		  _nums.size() - 1;
		int len2 = number._nums.size() - 1;
		int next = 0;						// 借位

		std::string copy;
		copy.reserve((len1 > len2 ? len1 : len2) + 5);

		while (len1 >= 0 || len2 >= 0)
		{
			int num1 = len1 >= 0 ?		  _nums[len1--] - '0' : 0;
			int num2 = len2 >= 0 ? number._nums[len2--] - '0' : 0;
			int ret = change * (num1 - num2) + next;

			if (ret < 0)					// 当前位为负数,向上借 1
			{
				next = -1;					// 上一位被借 1,需要 -1
				ret += 10;					// 上一位借的 1 ,在当前位为 10 
			}
			else							// 不为负数则不用借
			{
				next = 0;
			}

			copy += (ret + '0');
		}

		//最高位去零
		while (copy.size() > 1 && copy[copy.size() - 1] == '0')
		{
			copy.pop_back();
		}
		if (change == -1)					// 变号
		{
			signChange();
		}

		reverse(copy.begin(), copy.end());
		_nums = std::move(copy);
		return *this;
	}

	Big_integer& Big_integer::operator*=(const Big_integer& number)
	{
		if (*this == 0 || number == 0)	// 有 0 时直接返回 0
		{
			return *this = 0;
		}

		// 符号问题只需关注*this
		if (_sign == positive && number._sign == negative)
		{
			_sign = negative;
		}
		else if (_sign == negative && number._sign == negative)
		{
			_sign = positive;
		}

		int len1 =		  _nums.size() - 1;
		int len2 = number._nums.size() - 1;

		int maxN = (len1 + 1) + (len2 + 1);
		int index = maxN - 1;
		std::string copy(maxN, '0');

		// 乘法
		while (len1 >= 0)
		{
			int next = 0;
			int i = maxN - index - 1;
			int num1 = len1 >= 0 ? _nums[len1] - '0' : 0;

			for (int line = len2; line >= 0; --line)
			{
				int num2 = line >= 0 ? number._nums[line] - '0' : 0;
				int ret = num1 * num2 + next;

				next = ret / 10;
				ret = ret % 10;

				next +=   ((copy[i] - '0') + ret) / 10;
				copy[i] = ((copy[i] - '0') + ret) % 10 + '0';
				++i;
			}
			if (next > 0)
			{
				copy[i] += next;
			}
			--index;
			--len1;
		}

		// 最高位去零
		if (copy[copy.size() - 1] == '0')
		{
			copy.pop_back();
		}
		reverse(copy.begin(), copy.end());
		_nums = std::move(copy);
		return *this;
	}

	Big_integer& Big_integer::operator/=(const Big_integer& number)
	{
		// 除数不能为零
		assert(number != 0);

		// 除数大于被除数直接为零
		if (justThanSize(number) < 0)
		{
			return *this = 0;
		}

		Big_integer_sign sign1 = _sign;
		Big_integer_sign sign2 = number._sign;

		int len1 =		  _nums.size() - 1;
		int len2 = number._nums.size() - 1;
		int condition = len1 - len2;		// 对齐
		int count = 0;

		std::string copy;
		copy.reserve(len1);

		while (len1 - len2 - count >= 0)
		{
			int left = -1;
			int right = 10;
			while (left + 1 < right)		// 二分试乘
			{
				int mid = left + (right - left) / 2;
				if (justThanSize(number * mid * _pow(10, condition)) >= 0)
				{
					left = mid;
				}
				else
				{
					right = mid;
				}
			}
			if (left != 0)					// 结果减去试乘的数
			{
				if (_sign == number._sign)
				{
					*this -= (left * number) * _pow(10, condition);
				}
				else
				{
					*this += (left * number) * _pow(10, condition);
				}
			}

			copy += (left + '0');
			++count;
			--condition;
		}

		// 除后符号放后面,防止加减乘过程中为零负号变为正号
		if (sign1 == positive && sign2 == negative)
		{
			_sign = negative;
		}
		else if (sign1 == negative && sign2 == negative)
		{
			_sign = positive;
		}
		else
		{
			_sign = sign1;
		}

		if (copy[0] == '0')					// 最高位去零
		{
			copy.erase(copy.begin());
		}
		_nums = std::move(copy);
		return *this;
	}

	Big_integer& Big_integer::operator%=(const Big_integer& number)
	{
		Big_integer temp = *this;
		temp /= number;

		return *this -= temp * number;
	}

	Big_integer operator+(const Big_integer& number1, const Big_integer& number2)
	{
		return Big_integer(number1) += number2;
	}

	Big_integer operator-(const Big_integer& number1, const Big_integer& number2)
	{
		return Big_integer(number1) -= number2;
	}

	Big_integer operator*(const Big_integer& number1, const Big_integer& number2)
	{
		return Big_integer(number1) *= number2;
	}

	Big_integer operator/(const Big_integer& number1, const Big_integer& number2)
	{
		return Big_integer(number1) /= number2;
	}

	Big_integer operator%(const Big_integer& number1, const Big_integer& number2)
	{
		return Big_integer(number1) %= number2;
	}

	bool operator! (const Big_integer& number)
	{
		return number == 0;
	}

	bool operator!=(const Big_integer& number1, const Big_integer& number2)
	{
		return !(number1 == number2);
	}

	bool operator>=(const Big_integer& number1, const Big_integer& number2)
	{
		return !(number1 < number2);
	}

	bool operator> (const Big_integer& number1, const Big_integer& number2)
	{
		return !(number1 <= number2);
	}

	bool operator<=(const Big_integer& number1, const Big_integer& number2)
	{
		return number1 < number2 || number1 == number2;
	}

	bool operator==(const Big_integer& number1, const Big_integer& number2)
	{
		if (number1._sign == number2._sign && number1._nums == number2._nums)
		{
			return true;
		}
		else
		{
			return false;
		}
	}

	bool operator< (const Big_integer& number1, const Big_integer& number2)
	{
		if (number1._sign == Big_integer::negative && number2._sign == Big_integer::positive)
		{
			return true;
		}
		if (number1._sign == Big_integer::positive && number2._sign == Big_integer::negative)
		{
			return false;
		}

		int lessNum = number1.justThanSize(number2);
		if (lessNum < 0)
		{
			return number1._sign == Big_integer::positive ? true : false;
		}
		if (lessNum > 0)
		{
			return number1._sign == Big_integer::positive ? false : true;
		}
		return false;
	}

	std::ostream& my::operator<<(std::ostream& out, const my::Big_integer& number)
	{
		if (number._sign == my::Big_integer::negative)
		{
			out << '-';
		}
		out << number._nums;
		return out;
	}

	std::istream& my::operator>>(std::istream& in, my::Big_integer& number)
	{
		std::string temp;
		in >> temp;
		number = temp;
		return in;
	}
}

五、测试准确性

如有错误或Bug,可以评论区提出,谢谢!!!
在 test.cpp 中:

#include "Big_integer.h"
using namespace std;

void testAddSub()
{
	long long a = 0;
	long long b = 0;

	my::bint c = 0;
	my::bint d = 0;
	int count1 = 0;
	int count2 = 0;

	for (int i = 0; i < 100000; ++i)
	{
		a = ((rand() % 10000) * 100 + (rand() % 10000)) * 1000 + (rand() % 10000);
		b = ((rand() % 10000) * 100 + (rand() % 10000)) * 1000 + (rand() % 10000);
		c = a;
		d = b;
		if (c + d == a + b)
		{
			++count1;
		}
		else
		{
			cout << "a = " << a << " " << "b = " << b << endl;
			cout << "c = " << c << " " << "d = " << d << endl;
			cout << "a + b == " << a + b << endl;
			cout << "c + d == " << c + d << endl;
		}
		if (c - d == a - b)
		{
			++count2;
		}
		else
		{
			cout << "a = " << a << " " << "b = " << b << endl;
			cout << "c = " << c << " " << "d = " << d << endl;
			cout << "a - b == " << a - b << endl;
			cout << "c - d == " << c - d << endl;
		}
	}
	cout << "count1 == " << count1 << endl;
	cout << "count2 == " << count2 << endl;
}

void testMul()
{
	int a = 0;
	int b = 0;
	my::bint c = 0;
	my::bint d = 0;
	int count = 0;

	for (int i = 0; i < 100000; ++i)
	{
		int getSign1 = rand() % 2;
		int getSign2 = rand() % 2;
		a = rand();
		b = rand();
		if (getSign1 == 1)
		{
			a = -a;
		}
		if (getSign2 == 1)
		{
			b = -b;
		}
		c = a;
		d = b;
		if (c * d == a * b)
		{
			++count;
		}
		else
		{
			cout << "a = " << a << " " << "b = " << b << endl;
			cout << "c = " << c << " " << "d = " << d << endl;
			cout << "a * b == " << a * b << endl;
			cout << "c * d == " << c * d << endl;
		}
	}
	cout << "count == " << count << endl;
}

void testdiv()
{
	long long a = 0;
	long long b = 0;

	my::bint c = 0;
	my::bint d = 0;
	int count = 0;

	for (int i = 0; i < 10000; ++i)
	{
		int getSign = rand() % 2;
		int getSign2 = rand() % 2;
		a = ((rand() % 10000) * 100 + (rand() % 10000)) * 1000 + (rand() % 10000);;
		b = ((rand() % 10000) * 100 + (rand() % 10000)) * 1000 + (rand() % 10000);;
		if (getSign == 1)
		{
			a = -a;
		}
		if (getSign2 == 1)
		{
			b = -b;
		}
		c = a;
		d = b;
		if (c / d == a / b)
		{
			++count;
		}
		else
		{
			cout << "a = " << a << " " << "b = " << b << endl;
			cout << "c = " << c << " " << "d = " << d << endl;
			cout << "a / b == " << a / b << endl;
			cout << "c / d == " << c / d << endl;
		}
	}
	cout << "count == " << count << endl;
}

void testImp()
{
	long long a = 0;
	long long b = 0;

	my::bint c = 0;
	my::bint d = 0;
	int count = 0;

	for (int i = 0; i < 100000; ++i)
	{
		int getSign = rand() % 2;
		int getSign2 = rand() % 2;
		a = ((rand() % 10000) * 100 + (rand() % 10000)) * 1000 + (rand() % 10000);
		b = ((rand() % 10000) * 100 + (rand() % 10000)) * 1000 + (rand() % 10000);
		if (getSign == 1)
		{
			a = -a;
		}
		if (getSign2 == 1)
		{
			b = -b;
		}
		c = a;
		d = b;
		if (c % d == a % b)
		{
			++count;
		}
		else
		{
			cout << "a = " << a << " " << "b = " << b << endl;
			cout << "c = " << c << " " << "d = " << d << endl;
			cout << "a % b == " << a % b << endl;
			cout << "c % d == " << c % d << endl;
		}
	}
	cout << "count == " << count << endl;
}

int main()
{
	srand((unsigned int)time(NULL));

	testAddSub();
	testMul();
	testdiv();
	testImp();

	return 0;
}

六、优化方向

  1. 将数据倒着存储,可以减少每次计算后翻转效率的损失。

  2. 乘除使用更优的算法。

  3. 使用内置类型进行计算,提高计算速度,减少储存的浪费。

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

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

相关文章

Character AI被起诉!14岁青少年自杀,AI陪伴何去何从

终于&#xff0c;AI在青少年心理问题方面&#xff0c;被推上了风口浪尖。 最近&#xff0c;美国佛罗里达州&#xff0c;一名14岁男孩Sewell Setzer的父母控告Character AI公司&#xff0c;声称孩子沉迷该公司的AI聊天机器人&#xff0c;最后走上了自杀的道路。 跟AI聊天还能致…

基于STM32F103的按键检测仿真

基于STM32F103的按键检测 仿真软件&#xff1a; Proteus 8.17 编程软件&#xff1a; Keil 5 仿真实现&#xff1a; 按键第一次按下LED灯亮&#xff0c;第二次按下LED灯灭&#xff0c;往复 按键检测原理&#xff1a; 按键在电路中一端接单片机的IO口&#xff0c;一端接GND&a…

高效库存管理:金蝶云星空与管易云的盘亏单对接方案

高效库存管理&#xff1a;金蝶云星空与管易云的盘亏单对接方案 金蝶云星空与管易云的盘亏单对接方案 在企业日常运营中&#xff0c;库存管理是至关重要的一环。为了实现高效、准确的库存盘点和数据同步&#xff0c;我们采用了轻易云数据集成平台&#xff0c;将金蝶云星空的数据…

JavaWeb合集20-定时任务框架SpringTask

二十、Spring Task 定时任务框架&#xff1a;Spring Task是Spring框架提供的任务调度工具&#xff0c;可以按照约定的时间自动执行某个代码逻辑。 1、Spring Task cron 表达式 cron表达式在线生成器: https://cron.qqe2.com/ cron表达式其实就是一个字符串&#xff0c;通过cron…

极值点与拐点 专题小结

基础知识点 极值点与拐点的知识点 极值点相关概念了解 拐点相关概念了解极值点定义了解 二次函数求根公式理解 1、两个交点的解 2、单个交点的解 3、无交点的情况 含参数的函数 含参二次函数与x轴交点个数影响正负性变化&#xff0c;从而达成条件是否有极值点和拐点。 多重…

优先算法——移动零(双指针)

目录 1. 题目解析 2. 算法原理 3.代码实现 题目: 力扣题目链接&#xff1a;移动零 1. 题目解析 题目截图如下&#xff1a; 不过要注意&#xff0c;这个移动题目要求是在原数组中原地操作&#xff0c;不能新额外开辟一个数组来修改。 2. 算法原理 这个原理可以称之为数…

qt 滚动条 美化

qt QScrollBar 滚动条分为竖直与水平滚动条&#xff0c;两者设置上类似&#xff0c;但也有一些不同&#xff0c;下面主要讲述美化及注意事项。 一、竖直滚动条 竖直滚动条分为7个部分&#xff1a; sub-line、 up-arrow 、sub-page、 hanle、 add-line、 dow-arrow、 add-pag…

SQLI LABS | Less-8 GET-Blind-Boolian Based-Single Quotes

关注这个靶场的其它相关笔记&#xff1a;SQLI LABS —— 靶场笔记合集-CSDN博客 0x01&#xff1a;过关流程 输入下面的链接进入靶场&#xff08;如果你的地址和我不一样&#xff0c;按照你本地的环境来&#xff09;&#xff1a; http://localhost/sqli-labs/Less-8/ 靶场提示 …

前端实现监控埋点

前端实现监控埋点 前言 我们在应用开发完成本地测试跑通以后上线&#xff0c;线上可能会出现一些测试没有测出来的问题&#xff0c;那么这个时候我们如何定位到哪里会出现问题呢&#xff0c;因为在测试环境可能浏览器的不同&#xff0c;或是没有做兜底出现了一些线上的问题&a…

TLS协议基本原理与Wireshark分析

01背 景 随着车联网的迅猛发展&#xff0c;汽车已经不再是传统的机械交通工具&#xff0c;而是智能化、互联化的移动终端。然而&#xff0c;随之而来的是对车辆通信安全的日益严峻的威胁。在车联网生态系统中&#xff0c;车辆通过无线网络与其他车辆、基础设施以及云端服务进行…

取消element-ui中账号和密码登录功能浏览器默认的填充色,element-ui登录账号密码输入框禁用浏览器默认填充色问题

标题 问题展示 修改后 <div class="loginForm"><el-formref="formB":model="formDataB":rules="rulesB"class="login-form"label-position="left"><el-form-item prop="userNo" clas…

Spring Boot与Flyway实现自动化数据库版本控制

一、为什么使用Flyway 最简单的一个项目是一个软件连接到一个数据库&#xff0c;但是大多数项目中我们不仅要处理我们开发环境的副本&#xff0c;还需要处理其他很多副本。例如&#xff1a;开发环境、测试环境、生产环境。想到数据库管理&#xff0c;我们立刻就能想到一系列问…

Java最全面试题->Java基础面试题->JavaWeb面试题->Maven面试题

文章目录 Maven什么是maven&#xff1f;maven优缺点&#xff1f;maven常见的依赖范围有哪些?maven 坐标的含义?maven 常用命令?maven构建的过程&#xff1f;maven的生命周期&#xff1f;使用“mvn clean package”命令进行项目打包&#xff0c;该命令具体做了什么&#xff1…

Tcp_Sever(线程池版本的 TCP 服务器)

Tcp_Sever&#xff08;线程池版本的 TCP 服务器&#xff09; 前言1. 功能介绍及展示1.1 服务端连接1.2 客户端连接&#xff08;可多个用户同时在线连接服务端&#xff09;1.3 功能服务1.3.1 defaultService&#xff08;默认服务&#xff09;1.3.2 transform&#xff08;大小写转…

【STM32 ADC】

STM32 ADC功能简介 文章目录 前言一、ADC简介二、逐次逼近型ADC三、STM32的ADC内部框图四、STM32ADC输入通道五、规则组的四种转换模式六、规则组的触发源七、数据对齐八、转换时间九、校准部分十.ADC采样测量电压的程序设计十一、ADC的迟滞比较、滤波设计十二、四通道ADC采用问…

【github小问题】——push后报错error: src refspec master does not match any

温馨提示&#xff1a;这个问题可能有多种问题导致如未commit&#xff0c;本文在此讲述的是我遇到的这一种情况。 一、问题描述 从本地上传文件至github仓库时&#xff0c;add和commit都执行了且成功&#xff0c;但是执行git push -u origin master后出现了&#xff1a;error: …

深入理解JavaScript:两大编程思想和ES6类以及对象概念解析

文章目录 两大编程思想ES6中的类和对象 两大编程思想 面向过程 &#xff08;Procedural-Oriented Programming&#xff0c;POP&#xff09; 定义&#xff1a;面向过程的编程是一种基于过程调用的编程范式&#xff0c;它将程序看作是一系列函数或过程的集合。每个函数负责完成…

【K8S系列】Kubernetes pod节点Unknown 问题及解决方案详解【已解决】

在 Kubernetes 中&#xff0c;Pod 的状态为 Unknown 表示无法获取 Pod 的当前状态。这通常意味着 Kubernetes API 服务器无法与 Pod 所在的节点通信&#xff0c;或者 Kubelet 进程遇到问题。以下将详细介绍 Unknown 状态的原因、解决方案以及如何配置健康检查以提高系统的稳定性…

函数的实参和形参

什么是实参&#xff1f;什么是形参&#xff1f; 其实让我用语言来形容并不好描述&#xff0c;我们看例子&#xff1a; int add(int x , int y)//括号内就是形参 { int zxy; return z; } #include <stdio.h> int main() { int a8; int b9; int vadd(a,b);//括号内放置的参…

django-vue-admin测试环境搭建

django-vue-admin测试环境搭建 引言开发工具入门demo示例踩过的坑数据库字符集创建数据表前端路由 自定义app效果展示 引言 django-vue-admin框架&#xff0c;大幅度降低应用层代码难度,让每一个刚开始学习 django和vue的新手都能快速上手。这将会是你上手学习 djangovue的最佳…