首页 > 编程语言 >大数科学计算器 C++

大数科学计算器 C++

时间:2023-03-08 13:12:04浏览次数:56  
标签:return 大数 int C++ sign MyNum 计算器 const other

大数计算器

目录

优点

  • 实现了任意大数字计算 突破了longlong的-92233720368547758089223372036854775807的限制

  • MyNum类的实现 尤其是内部数据成员的使用思路 是程序的关键也是核心部分 同样的数据结构能实现三种不同的功能

  • MyNum类的存储方式受到了IEEE754标准下浮点数存储方式的启发

  • 可以判断逻辑条件表达式 例如 1+1 >= 2 返回TRUE

  • 计算的时候可自定义精度

  • 输出可自定义精度 实现四舍五入 (这个跟上一点不重复 比如最后的答案是0.00079 我就可以输出0.0008 但这并不改变这个数字是0.00079的事实 只是输出变了)

  • 计算过程中运用了后缀表达式法也就是逆波兰表达式法 实现了优先级计算

  • 重载了+-*/^等基础运算符 大大提高代码可读性以及复用性

  • 自定义函数有很多使用了自定义实现

    • 对于三角函数 e^x函数 等一些函数 舍弃了<cmath>库自带的函数 选择了利用泰勒展开数学迭代法进行自定义实现

    • 针对三角函数不但有泰勒展开 还进行了π优化 (比如27.75π化为1.75π) 大大提高精确度

    • 对于求幂指数符号^ 实现了小数指数幂的计算(可以计算 20.412^9.423 这种复杂式子)

      内部思路是 指数拆为整数+小数 整数部分运用快速幂算法进行优化 小数部分运用log()函数以及泰勒展开进行求解

    • 实现了最小公倍数lcm 最大公约数gcd 这些有多个参数的多目函数 其中的算法是辗转相除法

  • 实现了查询历史记录功能 输入history即可查询本次运行期间运算的记录

  • 实现了多重错误输出功能 不然可以输出程序错误 而且同时产生多重错误时可以全部输出

实现思路

自定义类

运用C++ 类的封装 自定义了两个类: MyNum类 和 Method

下面是这两个大类的简单归纳总结

MyNum类 ★★★

  • MyNum类起到三种作用: 1. 存放单个数字 2. 存放计算符号与函数(在计算时候的后缀表达式有大用处) 3.作为错误ERROR并且可以指示是何种错误

  • 内部数据成员

    std::deque<int> digits;
    int power;
    int sign;
    

    这个数据结构存储很有说法 哥们很引以为豪啊 虽然后面写函数的时候常常猪脑过载

    同样的三个数据成员 在三种不同作用的时候分别担任三种不同的工作 这三种不同作用是按照sign的值来区分的

    • MyNum作用是存放单个数字时 sign == -1 0 1 分别表示 负数,零和正数

      这个时候为了使MyNum既能表示整数也能表示小数 而且还要满足大数的要求

      首先存储数字使用了std::deque这个双向队列数据结构 他用起来的时候跟普通的数组差不多 而且比他功能更强大 所以就没用std::vector了 因为这个能存很多数字 所以就能实现大数的要求 不过之后的运算全都必须数组运算

      然后就是这个数据结构的思路 思路启发是IEEE754标准下的浮点数表示法:

      模仿的结果就是

      将一个数字abcde.fghij 表示成 0.abcdefghij * 10 ^ power 的形式

      跟IEEE一样并非传统的科学计数法(在小数点的前面留一个数字) 之前学的时候我不理解为什么 现在做这个计算器明白了

      IEEE舍弃掉整数位因为必定是1 但除此以外其实还有个好处

      如果是 0.xxxx 而不是 x.xxx 的话 这个指数power就很有用

      0.000233 = 0.233 * 10 ^ -3 指数 -3 拿出来可以瞬间知道 0.000233的小数点后面有3个0

      2333.3333 = 0.23333333 * 10 ^ 4 指数 4 拿出来可以瞬间知道 2333.3333的小数点前面有4位数

      std::deque<int> digits 存放"有效数字" (不包括开头和末尾的所有0 比如1000只存个1 方便后续运算 所以不是数学上真正意义的有效数字)

      int power 存放指数

    • MyNum作用是存放计算符号和函数的时候 sign == 2

      这个时候 刚刚是存放有效数字的digits摇身一变 变成了存放符号名称的东西

      比如乘法符号 '*' digit里面就是一个单独的{ '*' } 把char类型转换为int存进去

      如果是函数类型的计算 比如 log 函数 那digit里面就是 { 'l', 'o', 'g' } 总之就是把名字给放进去

      digits 没啥好说的 最最最关键的是这时候的power

      power此时不再存放指数(因为计算符号哪里有什么指数) 而是存放这个运算的优先级!!!! 这个在之后的后缀表达式求计算式答案的时候有大大大用

      // 里面定义的各种符号优先级如下:
      -1: 左括号(
      0:  各种逻辑判断符 >= < != 等 	//对你没有看错 还能求逻辑运算式
      1:  加减+-
      2:  乘除*/	
      3:  乘方^
      4:  各种函数运算 比如对数log 开根sqrt 三角函数sincos等
      5:  百分号%  //这个符号很迷其实 因为一般百分号是取模 但这里放优先级5是只让他求"百分之"的功能没有取模的功能  所以这里比较随意不严谨 之后可以优化一下
      

      他有什么好处呢? 后缀表达式那里就体现出来了

    • MyNum作用是作为错误ERROR的时候 sign == -2

      这个时候power没啥用(主要是我也不知道能给他啥用) 但digits就十分重要

      利用digits能像数组一样存储多个int的性质 我们给他存入类似于 {0,0,1,1,0,1,...}这些玩意儿 这是干啥的?

      这里 不同位置对应不同的错误类型 比如这个数组里面第3位是1 那么说明此时发生了第3种错误; 第5位是0说明没有第5种错误

      // 定义的各种错误类型顺序如下
      0: 出现除以0
      1: 出现0的0次幂
      2: 出现负数的分数次幂		//因为负数分数次幂就可能导致虚数  我干脆全给禁了
      3: 计算表达式非法			//有可能是括号配对错了 有可能是数字多了 总之表达式算不出来结果
      4: log函数出现非正底数		//就是ln(x)里面x必须大于0
      

      还可以加各种各样的错误 如果你有需要的就告诉我 我看看能不能加进来

    OK 以上就是MyNum类的数据成员的一些说法

  • 内置的一些函数

    • 构造函数 & 拷贝构造拷贝赋值析构三大函数

      主要都是提供方便

      其中最重要的是从字符串类型构造MyNum类型 因为丢进去的字符串可能是 "-233.333" 这种正常的数字 也可能是 "sin()" 这种函数计算符号 所以它要作区分并生成正确的MyNum对象

    • 数学函数

      都是sqrt() fabs() 这种数学的函数 其中很多函数舍弃掉了<cmath>头文件中现有的函数 选择了自定义实现 目的就是提高精度!

      有些函数例如三角函数 e^x自然指数函数 这怎么自定义实现呢?

      方法: **泰勒展开!! ** 利用迭代 计算出泰勒展开中每项 然后就能得到自己想要精度的结果

      这个详细的过程在后面讲到函数实现的时候再说吧 目前看一下成果:

      准确率还是可以的

    • 转换函数

      就是MyNum类转换到其他类 比如什么int啊double啊啥的 主要也就是为了方便才整的 有要提的之后具体展开到函数实现再说

    • 运算符重载

      实现计算的时候肯定是用 + * 这些运算符来的方便 所以自定义的数字类必然要运算符重载

      也没啥太必要提的 还是之后函数实现细说 不过里面有关幂指数的^符号的实现比较有含金量一些

    • 显示函数

      就是输出到黑框框里面给人看的函数 也没啥重要的 函数实现再细说

Method

  • Method类不像MyNum类有那么多功能 Method在数学上的意思是计算式表达式 Method类就是一个用来存储我们每一个计算式的类 比如6/3*(5+2.4-1)

  • 内部数据成员

    string calstr			
    std::deque<MyNum> rpn
    int type				//0 计算式  1 逻辑条件式
    MyNum ans				//计算结果
    
    • string calstr: 就是calculate string 需要计算的原式 这个没啥特殊的 顶多就是输入的时候把输入的空格啥的给删了

    • std::deque<MyNum> rpn: 这个very的重要 这个rpnReverse Polish Notation 逆波兰表示法/后缀表达式 我们后续的计算式求解思路 就是先把整个式子换成后缀表达式 然后进行求解

    • int type: 这个是用来指示这个计算式是什么个类型的 普通的计算式那就是算出个数字结果 但如果是1+1 >= 2这种逻辑式 就要把最后的1输出为"TRUE" 0就是"FALSE" 所以type就用0代表是计算式 1代表是逻辑条件式

    • MyNum ans: 这个更不用多说了 有计算式肯定要有结果的嘛 这个就是这个式子的结果

  • 内置的一些函数

    • 构造 & 三大函数

      没啥特殊的 过

    • 操作函数

      无非就是计算啊啥的

      不过里面最最最重要的 就是把原计算式转换为后缀表达式的那个函数 他是一切计算的起点 在之后函数实现的时候细说

    • 显示函数

      就是显示原式 显示答案之类的

      不过用户可以设定一个参数 来规定输出的类型 可以是简洁模式(按下回车之后只有答案) 也可以是详细模式(按下回车之后除了答案还能看到 原式以及原式变为的后缀表达式)

      除此以外 还可以在黑框框那边输入 setprecision 数字 来规定输出小数的精度 有四舍五入

具体函数实现

MyNum类的函数

构造 & 三大函数
//构造函数
	MyNum(const std::deque<int>& d=ZERODEQUE,int p=0,int s=0)
		:digits(d),power(p),sign(s){}
	MyNum(int i);
	MyNum(long long i);
	MyNum(double d);
	MyNum(std::string str);

//拷贝构造拷贝赋值
	MyNum(const MyNum& other)
		:digits(other.digits), power(other.power),sign(other.sign) {}
	MyNum& operator= (const MyNum& other) {
		this->digits = other.digits;
		this->power = other.power;
		this->sign = other.sign;
		return *this;
	}

都比较普通 能拿出来说的也就是从字符串string构造MyNum

总体思路:

  • 判断string是否是类似 "fabs"的计算函数 或者 类似"+-*/"的计算符号

    • 如果是 那么sign赋值为2 表示这个MyNum是一个计算用的符号 接下来根据不同符号的不同优先级来定下MyNum中的power参数 接着把这个string变成int数组存进digits里面 这个在上面将MyNum的数据成员的时候细说过 如果忘了可以回去看看

    • 如果不是 说明这个string是个类似 "-233.333" 这样的数字 那么首先看正负定下sign 然后就要把有效数字放进digits 找到指数放进power里

      难点就在于定下指数power 可以把整数部分剥离开来 之后就好求digits了

源码:

MyNum::MyNum(std::string str) {
	if (str.size() && (str[0] < '0' || str[0]>'9')) {		//string不是数字是个计算符号
		if (!((str[0] == '-' || str[0] == '+') && str.size() > 1 && str[1] >= '0' && str[1] <= '9')) {
			this->sign = 2;
			if (str[0] >= 'a' && str[0] <= 'z' || str[0] >= 'A' && str[0] <= 'Z') 	//函数类型计算  优先级4
				power = 4;
			else {
				if (str[0] == '(') power = -2;
				if (str[0] == '=' || str[0] == '<' || str[0] == '>') power = 0;
				if (str[0] == '+' || str[0] == '-') power = 1;
				if (str[0] == '*' || str[0] == '/') power = 2;
				if (str[0] == '^') power = 3;
				if (str[0] == '%') power = 5;
				if (str[0] == ',')	power = -1;				//根据不同的符号 确定各自的优先级
			}
			for (int i = 0; i < str.size(); i++) {
				this->digits.push_back(int(str[i]));		//将名字存进digits里面
			}
			return;
		}
	}
    //下面就是str表示的是数字情况
	this->sign = 1;
	int i = 0;
	while (str[i] == ' ') i++;
	if (str[i] == '-') {
		this->sign = -1; i++;
	}
	if (str[i] == '+') i++;

	int retpower = 0;
	bool startdigits = false;	//开始有效数字
	int powerchange = 1;		//1表示每往后一格power++  -1就是小数情况
	int ifdecimal = false;
	bool ifmeetpoint = false;	//有无遇见小数点
	while (i < str.size()) {
		if (startdigits && str[i]>='0' && str[i]<='9') {
			this->digits.push_back(str[i] - '0');
			if (!ifmeetpoint) 
				retpower++;
		}
		else {
			if (str[i] >= '1' && str[i] <= '9') {
				startdigits = true;
				this->digits.push_back(str[i] - '0');
				if (!ifmeetpoint)
					retpower++;
				if (ifdecimal) {
					this->power = retpower;
				}
			}
			if (str[i] == '0' && ifdecimal) {
				retpower--;
			}
			if (str[i] == '.') {
				ifmeetpoint = true;
				if (!startdigits) {	//小数
					ifdecimal = true;
				}
				else {
					this->power = retpower;
				}
			}
		}
		i++;
	}
	while (!this->digits.empty() && this->digits.back() == 0)		//去除末尾的0 保证digits里是纯纯的有效数字
		this->digits.pop_back();
	if (digits.empty()) {		//如果去掉0之后发现没东西了  那么说明这个数字就是0 返回ZERO零常量
		*this = ZERO;
	}
	this->power = retpower;
	this->setPrecision();		//规定小数位数精度  毕竟无限小数不能有无限大的digits给你用
}
数学函数
//数学函数
	static MyNum getPI();	//求π
  //单目数学函数
	MyNum My_sqrt() const;
	MyNum My_sin() const; MyNum My_cos() const; MyNum My_tan() const;
	MyNum My_exp() const;
	MyNum My_fabs() const;
	MyNum My_neg() const; 
	MyNum My_log() const;
  //多目数学函数
	friend MyNum My_gcd(const MyNum& a, const MyNum& b);
	friend MyNum My_lcm(const MyNum& a, const MyNum& b);

其中值得拿出来说的一些函数是

  • 求π值函数getPI()

    这个函数是求出π之后在三角函数里实现π优化用的 实现方法是

    证明过程如下:

    源码:

    MyNum MyNum::getPI() {		//危!!! 会超时
    	MyNum ret;
    	MyNum add(1);
    	for (int i = 1; add.sign != 0; i+=2) {
    		add = ONE / MyNum(i);
    		if ((i / 2) % 2)
    			ret -= add;
    		else
    			ret += add;
    	}
    	return ret*MyNum(4);
    }
    

    但是一定一定要注意 这个函数..... 不能实际应用出来 因为要算出来一个精度够用的π 时间会爆炸....

    求出来一个后20位的π 要这个里面跑tm好几小时 鬼才用..... 所以代码里我直接把π的真实值给拿来用了哈哈哈哈哈哈哈

  • 自然指数函数exp()

    这个有现成的exp() 不过返回的double有精度的限制 所以使用了泰勒展开进行迭代从而求出我们想要精度的结果

    源码:

    MyNum MyNum::My_exp() const { 
    	if (this->sign == 0) return ONE;	//e^0=1
    	if(*this>MyNum(TAYLOR_LIMIT))
    		return MyNum(exp(this->to_double()));	//如果x太大那么就直接用内置函数了  不然迭代计算量太大
    	MyNum ret;
    	MyNum add(1);
    	for (int i = 1; add.sign != 0; i++) {		//注意这里的跳出条件 如果add太小了变成0了 说明我们迭代到我们要的精度了 这个时候就可以跳出
    		ret += add;
    		add *= (*this) / MyNum(i);		//迭代
    	}
    	return ret;
    }
    

    看起来还是很简单的

  • 三角函数sin() cos() tan()

    首先 也是用了泰勒展开来求他们的值

    除此以外 针对三角函数x+2kπ的特性 也进行了π优化 把数字控制在[-2π,2π]之间

    源码:

    MyNum MyNum::My_sin() const {
    	if (this->sign == 0) return ZERO;	//sin(0)=0
    	MyNum cal(*this);
    	while (cal < -PIDOUBLE)
    		cal += PIDOUBLE;
    	while (cal > PIDOUBLE) 
    		cal -= PIDOUBLE;	//PI优化
    	MyNum ret;
    	MyNum add(-1);
    	for (int i = 1; add.sign != 0; i++) {	//泰勒展开迭代过程 跳出条件还是跟exp一样 add为0说明精度够了
    		add = add * cal / MyNum(i);
    		if (i % 2) {
    			add.sign *= -1;
    			ret += add;
    		}
    	}
    	return ret;
    }
    MyNum MyNum::My_cos() const{ 
    	if (this->sign == 0) return ZERO;	//cos(0)=1
    	MyNum cal(*this);
    	while (cal < -PIDOUBLE)
    		cal += PIDOUBLE;
    	while (cal > PIDOUBLE)
    		cal -= PIDOUBLE;	//PI优化
    	MyNum ret(1);
    	MyNum add(1);
    	for (int i = 1; add.sign != 0; i++) {	//同上 泰勒展开部分
    		add = add * cal / MyNum(i);
    		if (i % 2 == 0) {
    			add.sign *= -1;
    			ret += add;
    		}
    	}
    	return ret;
    }
    MyNum MyNum::My_tan() const{ 	
    	return this->My_sin() / this->My_cos();		//tan就别泰勒展开了  会寄掉的 直接sin/cos
    }
    

    效果拔群:

  • 最大公约最小公倍gcd() lcm()

    主要就是一个辗转相除法

    没啥说的 直接放源码吧:

    MyNum My_gcd_recursion(MyNum& a, MyNum& b) {	//递归辗转相除
    	while (a > b)
    		a -= b;
    	if (a == b) return a;
    	else return My_gcd_recursion(b, a);
    }
    MyNum My_gcd(const MyNum& a,const MyNum& b) {
    	if (a.sign != b.sign) return ERROR;
    	MyNum aa(a.digits,a.power,1),bb(b.digits,b.power,1);
    	MyNum ret(My_gcd_recursion(aa, bb));
    	ret.sign = a.sign;
    	return ret;
    }
    MyNum My_lcm(const MyNum& a,const MyNum& b) {
    	return a * b / My_gcd(a, b);		//注意这里lcm有个数学关系: lcm*gcd=a*b
    }
    

    注意这里实现了一个神奇的功能: 小数也可以求最大公约最小公倍!

转换函数
//转换函数
	double to_double() const;
	int to_int() const;
	long long to_longlong() const;
	std::string to_string() const;
	MyNum decimalpart() const;
	MyNum addError(int i)const;

正常的转换函数没啥好说的

要说的是里面最后一个addError()

给他一个参数i i指的是要添加哪一种ERROR进去 什么叫哪一种ERROR? 在上文有说过 我们想要实现不同的错误类型的区分 所以就有了几个错误类型的划分 这里的i就是对应你要添加哪种错误类型

具体就是

如果是在原来ERROR的基础上添加一个新的错误类型 也就是要在digits的第i位置为1 那么先补全digits 然后置为1就好

源码:

MyNum MyNum::addError(int i)const {
	MyNum ret(ERROR);
	ret.digits.clear();
	if (this->sign == -2)
		ret = *this;
	for (int temp = ret.digits.size(); temp <= i; temp++) {		//补全digits到i
		ret.digits.push_back(0);
	}
	ret.digits[i] = 1;
	return ret;
}

至于ERROR的输出效果 可以先在这里展示一下

运算符重载
//运算符重载
	bool operator<(const MyNum& other)const;
	bool operator>(const MyNum& other)const;
	bool operator==(const MyNum& other)const;
	bool operator!=(const MyNum& other)const;
	bool operator<=(const MyNum& other)const;
	bool operator>=(const MyNum& other)const;
	MyNum operator+(const MyNum& other)const;
	MyNum operator-()const;
	MyNum operator-(const MyNum& other)const;
	MyNum operator*(const MyNum& other)const;
	MyNum operator/(const MyNum& other)const;
	MyNum operator^(const MyNum& other)const;
	MyNum power_int(int k)const;
	MyNum power_decimal(const MyNum& decimal)const;
	MyNum& operator+=(const MyNum& other);
	MyNum& operator-=(const MyNum& other);
	MyNum& operator*=(const MyNum& other);
	MyNum& operator/=(const MyNum& other);
  • 上面的几个大小判断 没啥好说的 就是注意符号问题

    下面的几个运算符有不少说头

  • +

    既然涉及到了大数字 我们这里就是两个deque的加法 那么本来最平平无奇的加号也变得有了学问起来

    总之核心思想就是三个字: 列竖式

    我们小数加法怎么列竖式的 我们就怎么在这里加法

    第一步就是对齐小数点 反映到程序上就是临时在短的数字前面补上0以此跟长数字长度一致

    下一步各位相加 加完之后从后往前把每一个进位给处理好 如果最后有进位再在返回的ans最前面push_front一个进位数即可 与此同时变一下指数power

    源码:

    MyNum MyNum::operator+(const MyNum& other)const {
    	//ERROR
    	if ((*this).sign == -2 || other.sign == -2) {
    		if ((*this).sign == -2 && other.sign == -2) {
    			MyNum reterror(ERROR);
    			for (int i = 0; i < this->digits.size(); i++) {
    				if (this->digits[i]) {
    					reterror = reterror.addError(i);
    				}
    			}
    			for (int i = 0; i < other.digits.size(); i++) {
    				if (other.digits[i]) {
    					reterror = reterror.addError(i);
    				}
    			}
    			return reterror;
    		}
    		if ((*this).sign == -2) return *this;
    		if (other.sign == -2) return other;
    	}
    	//异号
    	if (this->sign * other.sign == -1) {
    		if (this->sign < 0) return other - (-(*this));
    		else return (*this) - (-other);
    	}
    
    	//0
    	if (this->sign == 0) return other;
    	if (other.sign == 0) return *this;
    
    	//同号
    	//找到较大的那个数 用它加上较小的数 比较省事
    	MyNum const *bigger,*smaller;
    	if (this->My_fabs() >= other.My_fabs()) 
    		{ bigger = this; smaller = &other; }
    	else
    		{ bigger = &other; smaller = this; }
    	int retpower = bigger->power;
    	int retsign = bigger->sign;
    	//对齐小数点后逐位相加
    	int diff = bigger->power - smaller->power;		//小数点偏差
    	std::deque<int> retdigits = bigger->digits;
    	int length = 0;
    	while (length < diff) {
    		while (length >= retdigits.size())
    			retdigits.push_back(0);
    		length++;
    	}
    	for (int temp = 0; temp < smaller->digits.size(); temp++) {
    		while (length >= retdigits.size())
    			retdigits.push_back(0);
    		retdigits[length++] += smaller->digits[temp];
    	}
    	//从后往前逐位校准
    	for (int temp = length - 1; temp >= 1; temp--) {
    		retdigits[temp - 1] += retdigits[temp] / 10;
    		retdigits[temp] %= 10;
    	}
    	if (retdigits[0] >= 10) {
    		retdigits.push_front(retdigits[0] / 10);
    		retdigits[1] %= 10;
    		retpower++;
    	}
    	//末尾去0
    	while (!retdigits.empty() && retdigits.back() == 0)
    		retdigits.pop_back();
    	if (retdigits.empty()) return ZERO;
    
    	MyNum ans(retdigits, retpower, retsign);
    	ans.setPrecision();
    	return ans;
    }
    

    最前面的部分是重载了一下ERROR+ERROR的情况 方便之后使用

  • -

    减法跟加法大同小异 也是先对齐小数点然后逐位相减 然后从后往前处理 只是要注意一下处理完之后第一位被借没掉的情况 此时要变power

    源码:

    MyNum MyNum::operator-(const MyNum& other)const {
    	//ERROR
    	if ((*this).sign == -2 || other.sign == -2) return *this+other;
    
    	//异号
    	if (this->sign * other.sign == -1) {
    		if (this->sign < 0) return -(other + (-(*this)));
    		else return (*this) + (-other);
    	}
    
    	//0
    	if (this->sign == 0) return -other;
    	if (other.sign == 0) return -(*this);
    
    	//同号
    	if ((*this) == other) return ZERO;
    	int retsign;
    	//找到绝对值较大的那个数 用它减去较小的数 比较省事
    	MyNum const *bigger, *smaller;
    	if (this->My_fabs() > other.My_fabs()) 
    		{ bigger = this; smaller = &other; retsign = bigger->sign; }
    	else
    		{ bigger = &other; smaller = this; retsign = -bigger->sign; }
    	int retpower = bigger->power;
    	//对齐小数点逐位相减
    	int diff = bigger->power - smaller->power;		//小数点偏差
    	std::deque<int> retdigits = bigger->digits;
    	int length = 0;
    	while (length < diff) {
    		while (length >= retdigits.size())
    			retdigits.push_back(0);
    		length++;
    	}
    	for (int temp = 0; temp < smaller->digits.size(); temp++) {
    		while (length >= retdigits.size())
    			retdigits.push_back(0);
    		retdigits[length++] -= smaller->digits[temp];
    	}
    
    	//从后往前逐位校准
    	for (int temp = length - 1; temp >= 1; temp--) {
    		while (retdigits[temp] < 0) {
    			retdigits[temp - 1]--;
    			retdigits[temp] += 10;
    		}
    	}
    	//去头0 去尾0
    	while (!retdigits.empty() && retdigits.front() == 0) {
    		retdigits.pop_front();
    		retpower--;
    	}
    	while (!retdigits.empty() && retdigits.back() == 0) 
    		retdigits.pop_back();
    	if (retdigits.empty()) return ZERO;
    
    	MyNum ans(retdigits, retpower, retsign);
    	ans.setPrecision();
    	return ans;
    }
    
  • *

    还是列竖式 a的每一位数去乘b的每一位数 途中注意对齐问题 然后把所有的加起来 就是最终的结果 注意进位即可

    MyNum MyNum::operator*(const MyNum& other)const {
    	//ERROR
    	if ((*this).sign == -2 || other.sign == -2) return *this + other;
    
    	//0
    	if (this->sign == 0) return ZERO;
    	if (other.sign == 0) return ZERO;
    
    	int retsign = this->sign * other.sign;
    	int retpower = this->power + other.power;
    	std::deque<int> retdigits;
    	for (int i = 0; i < this->digits.size(); i++) {
    		if (this->digits[i] == 0) continue;
    		int temp = i;
    		for (int j = 0; j < other.digits.size(); j++) {
    			while (temp >= retdigits.size()) 
    				retdigits.push_back(0);
    			retdigits[temp++] += this->digits[i] * other.digits[j];
    		}
    	}
    	for (int temp = retdigits.size() - 1; temp >= 1; temp--) {
    		retdigits[temp - 1] += retdigits[temp] / 10;
    		retdigits[temp] %= 10;
    	}
    	retpower--;
    	while (retdigits.front() >= 10) {
    		retdigits.push_front(retdigits[0] / 10);
    		retdigits[1] %= 10;
    		retpower++;
    	}
    	MyNum ans(retdigits, retpower, retsign);
    	ans.setPrecision();
    	return ans;
    }
    
  • /

    除法算是比较难想的一个了 他也是列竖式 但是除法竖式本身就和之前的三种不一样 所以不是很容易

    也是除数一位位往右边挪 一步步翻倍除数 一直到他大于被除数 这个时候翻了几倍就是这个位数的答案 接着把除数往后挪一格 重复以上过程

    MyNum MyNum::operator/(const MyNum& other)const {
    	//ERROR
    	if ((*this).sign == -2 || other.sign == -2) return *this + other;
    
    	//除0
    	if (other.sign == 0) {
    		return ERROR.addError(0);
    	}
    	if (this->sign == 0) {
    		return ZERO;
    	}
    
    	if (other == MyNum(1)) return *this;
    	if (other == MyNum(-1)) return -*this;
    	int retsign = this->sign * other.sign;
    	int retpower = this->power - other.power;
    	std::deque<int> a = this->digits;
    	std::deque<int> b = other.digits;
    
    	std::deque<int> ans;
    	for (int temp = 0; temp <= PRECISION + retsign; temp++) {		//除法核心部分
    		int next = 0;
    		while (cmp_digits(a, b) > 0) {
    			minus_digits(a, b);
    			next++;
    		}
    		if (cmp_digits(a, b) == 0) {
    			next++; 
    			ans.push_back(next);
    			break;
    		}
    		ans.push_back(next);
    		b.push_front(0);
    	}
    
    	while (!ans.empty() && ans.front() == 0) {
    		ans.pop_front();
    		retpower--;
    	}
    	while (!ans.empty() && ans.back() == 0) {
    		ans.pop_back();
    	}
    	retpower++;
    	MyNum ret(ans, retpower, retsign);
    	ret.setPrecision();
    	return ret;
    }
    
  • ^

    之前提过了 这些个运算里面最最有含金量的就是乘方运算 因为他实现了小数指数的计算

    大致思路就是把小数指数 拆成整数和小数部分 其中整数部分操作的时候利用快速幂来进行优化 小数部分利用log()函数以及泰勒展开的技巧 迭代求出最后结果 两部分求出来之后最后乘到一起就是最终的结果

    源码:

    MyNum MyNum::operator^(const MyNum& other) const{
    	//ERROR
    	if ((*this).sign == -2 || other.sign == -2) return *this + other;
    
    	MyNum b(other);
    	if (b.sign == 0) return (this->sign == 0) ? ERROR.addError(1) : ZERO;
    	if (this->sign == 0) return (b.sign > 0) ? ZERO : ERROR.addError(0);
    	//仅排除了底数为零的情况 不保证幂指数的整数部分和小数部分不为0
    	if (b.sign > 0)
    		return power_int(other.to_int()) * (power_decimal(other.decimalpart()));	//整数部分快速幂 小数部分泰勒展开
    	else
    		return MyNum(1) / (power_int(-other.to_int()) * (power_decimal(-other.decimalpart())));
    }
    MyNum MyNum::power_int(int k)const {		//整数部分
    	if (k == 0) return MyNum(1);
    	if (k < 0) return MyNum(1) / ((*this) ^ (-k));
    	if (*this == MyNum(1)) return MyNum(1);
    	
    	//快速幂部分
    	MyNum ans(1);
    	MyNum bottom(*this);
    	while (k) {
    		if (k & 1) {
    			ans *= bottom;
    		}
    		bottom *= bottom;
    		k >>= 1;
    	}
    	ans.setPrecision();
    	return ans;
    }	
    MyNum MyNum::power_decimal(const MyNum& decimal) const{		//小数部分
    	//ERROR
    	if ((*this).sign == -2) return *this;
    	
    	if (decimal.sign == 0) return MyNum(1);
    	if (this->sign < 0)	//底数负数 不可进行小数幂
    		return ERROR.addError(2);
    
    	MyNum mult = this->My_log() * decimal;	//泰勒展开每一项要乘的东西
    	MyNum add(1);
    	MyNum ret;
    	for (int i = 1; add.sign != 0; i++) {
    		ret += add;
    		add = add * mult / MyNum(i);
    	}
    	return ret;
    }
    

    这个还是挺有含金量的

显示函数
//显示
	void show() const;
	void setPrecision(int prec = PRECISION);

都没啥好说的

show()里面有个显示ERROR内容 大致就是有一个存放各个error名字的数组 如果这个MyNum是ERROR 那么就根据他的digits去看对应了哪一种的ERROR 然后输出就行

源码:

void MyNum::show() const {
	if (this->sign==-2) {
		printf("ERROR\n");
		for (int i = 0; i < digits.size(); i++) {
			if (digits[i])
				std::cout << errorreason[i] << std::endl;
		}
		return;
	}
	if (sign == 2) {
		if (digits[0] == ',') return;
		for (int i = 0; i < digits.size(); i++)
			printf("%c", char(digits[i]));
		return;
	}
	int power = this->power;
	int sign = this->sign;
	std::deque<int> digits = this->digits;
	if (sign == -1) printf("-");
	if (sign == 0) { printf("0"); return; }

	if (power <= 0) {
		printf("0.");
		while (power < 0) {
			printf("0"); power++;
		}
		power--;
	}
	for (int i = 0; i < digits.size(); i++) {
		if (power-- == 0) {
			printf(".");
		}
		printf("%d", digits[i]);
	}
	while (power > 0) {
		printf("0");
		power--;
	}
}

Method类的函数

构造函数
//构造
	Method(string s,int t=0)
		:type(t){
		input(s);
		changetorpn();
		calculate();
	}

无要说的 就是正常的构造

操作函数
//操作函数
	void input(std::string str);		///输入 将字符串中计算部分提取出来
	void opstackpushback(std::deque<MyNum>& opstack, MyNum op);	//计算符号入栈
	void changetorpn();	//将计算式转换为后缀表达式
	MyNum calculate();	//计算

重中之重就是一个把原计算式转换为后缀表达式的changetorpn() 还有一个计算函数calculate()

  • changetorpn()

    简单思路: 通过遍历calstr计算式子字符串 把每个对应于数字的 对应于计算符号的 对应于计算函数的 全部一个个拆开来分成单独的MyNum 然后根据后缀表达式的转换规则 利用一个操作符单调栈opstack 按照操作符优先级的严格递增顺序进行入栈出栈 也就是严格单调栈 从而进行后缀表达式的转换

    具体可以参见这篇文章: https://blog.csdn.net/a8425/article/details/119253258

    源码:

    void Method::changetorpn() {
    	this->rpn.clear();
    	std::deque<MyNum>& retrpn = this->rpn;
    	std::deque<MyNum> opstack;	//符号栈(用deque模拟栈)
    
    	string nowdigits;
    	string op;
    	for (int i = 0; i < calstr.size(); i++) {
    		if (calstr[i] == ' ') continue;
    		if (calstr[i] == ',') {
    			if (!nowdigits.empty()) {
    				retrpn.push_back(MyNum(nowdigits));
    				nowdigits.clear();
    			}
    			while (!opstack.empty() && opstack.back() != MyNum("(")) {
    				retrpn.push_back(opstack.back());
    				opstack.pop_back();
    			}
    			//opstack.pop_back();
    			continue;
    		}
    		if (calstr[i] == '-' ) {
    			if (!(!nowdigits.empty() || i > 0 && calstr[i - 1] == ')')) {	//'-'是负号不是减号
    				if (!op.empty()) {
    					opstackpushback(opstack, MyNum(op));
    					op.clear();
    				}
    				opstackpushback(opstack, MyNum("neg"));
    				continue;
    			}
    		}
    		if (calstr[i] >= '0' && calstr[i] <= '9' ||calstr[i]=='.') {
    			nowdigits.push_back(calstr[i]);
    			if (!op.empty()) {
    				opstackpushback(opstack, MyNum(op));
    				op.clear();
    			}
    			continue;
    		}
    		if (!nowdigits.empty()) {
    			retrpn.push_back(MyNum(nowdigits));
    			nowdigits.clear();
    		}
    		if (calstr[i] == '(') {
    			if (!op.empty()) {
    				opstackpushback(opstack, MyNum(op));
    				op.clear();
    			}
    			opstack.push_back(MyNum("("));
    			continue;
    		}
    		if (calstr[i] == ')') {
    			if (!nowdigits.empty()) {
    				retrpn.push_back(MyNum(nowdigits));
    				nowdigits.clear();
    			}
    			while (!opstack.empty() && opstack.back() != MyNum("(")) {
    				retrpn.push_back(opstack.back());
    				opstack.pop_back();
    			}
    			if (opstack.empty()) {	//多出来个右括号
    				ans = ERROR.addError(3);
    				return;
    			}
    			opstack.pop_back();
    			if (!opstack.empty() && opstack.back().power == 4) {		//函数类计算后面是紧紧跟随括号的 所以括号被弹出后 函数也必须紧跟着弹出
    				retrpn.push_back(opstack.back());
    				opstack.pop_back();
    			}
    			continue;
    		}
    		//以下情况皆为calstr[i]为计算符号
    		if (i < calstr.size() && !judgeop(calstr[i])) {		//符号型计算
    			if (!op.empty() && judgeop(op[0])) {	//op中装着函数型计算
    				opstackpushback(opstack, MyNum(op));
    				op.clear();
    			}
    			else if(!op.empty()){		//原先有符号 但是无法凑在一起
    				string tempop(op); tempop.push_back(calstr[i]);
    				if (tempop != "<=" && tempop != ">=" && tempop != "==" && tempop != "!=") {
    					opstackpushback(opstack, MyNum(op));
    					op.clear();
    				}
    			}
    			op.push_back(calstr[i]);
    			continue;
    		}
    		if (i < calstr.size() && judgeop(calstr[i])) {		//函数型计算
    			if (!op.empty() && !judgeop(op[0])) {	//op中装着符号型计算
    				opstackpushback(opstack, MyNum(op));
    				op.clear();
    			}
    			op.push_back(calstr[i]);
    			continue;
    		}
    	}
    	
    	if (!op.empty()) {
    		opstackpushback(opstack, MyNum(op));
    		op.clear();
    	}
    	if (!nowdigits.empty()) {
    		retrpn.push_back(MyNum(nowdigits));
    		nowdigits.clear();
    	}
    	while (!opstack.empty()) {
    		if (opstack.back() == MyNum("(")) {
    			ans = ERROR.addError(3);
    			return;
    		}
    		retrpn.push_back(opstack.back());
    		opstack.pop_back();
    	}
    }
    
  • calculate()

    有了后缀表达式其实计算就简单很多 就是遍历后缀表达式rpn 利用一个辅助计算栈tempans 如果碰到计算符那么就按照计算符的运算规则进行计算 要两个数的就pop两个数出来 要一个数就pop一个

    源码巨长无比就不放了 但是主要都是不同计算的式子 没啥含金量 只是纯纯的长

显示函数
//显示
	void showCalstr();
	void showRPN();
	void showAns(int showprecision=SHOW_PRECISION);
	void show(int type = 0);

主要是显示各种东西 有把后缀表达式显示出来的 也有必需的显示答案用的

里面的SHOW_PRECISION就是用来更改显示答案精度的

主程序的功能 & 函数

主程序主要实现了两个重要功能:

  • 输入history即可查看此轮运行时候计算过的式子记录 其中底层原理其实就是一个deque里面 每计算一次就往里面push进一个Method 要看历史记录的时候就遍历输出
  • 输入setprecision 数字即可更改显示精度 这个在之前提过了

主程序有关运行的源码:

void showHistory() {
	for (auto i : history) {
		i.showCalstr(); printf("\nans = "); i.showAns(); cout << endl;
	}
}
int inputMethod() {
	char s[20000]{0};
	cout << ">> ";
	fflush(stdin);
	scanf("%[^\n]", s);
	string str(s);
	if (str.empty()) {
		getchar();
		return -1;
	}
	if (str == "end") 
		return 0;
	if (str == "history") {
		return 2;
	}
	if (str == "changeshowtype") {
		showType = 1 - showType;
		return 3;
	}
	if (str.size() > 12 && str.substr(0,12) == "setprecision") {
		int newprecision = 0;
		for (int i = 13; i < str.size(); i++) {
			newprecision *= 10;
			newprecision += str[i] - '0';
		}
		SHOW_PRECISION = newprecision;
		return 4;
	}
	Method m(str);
	history.push_back(m);
	return 1;
}

void Func() {
	int input = 1;
	while(true) {
		input = inputMethod();
		if (input == 0) break;
		if (input == 1) {
			history.back().show(showType);
			cout << endl;
			getchar();
		}
		if (input == 2) {
			printf("历史计算:\n");
			showHistory();
			getchar();
		}
		if (input == 3) {
			printf("已更改显示模式为:");
			if (showType == 1) printf("详细模式\n");
			else printf("简洁模式\n");
			getchar();
		}
		if (input == 4) {
			printf("输出小数精度已被设置为:%d位小数\n", SHOW_PRECISION);
			getchar();
		}
	}
}

int main() {
	showType = 0;
	SHOW_PRECISION = 6;		//输出精度默认为6

	Welcome();
	Func();
	return 0;
}

完整代码

MyNum.h

#ifndef _MYNUM_H_

#define _MYNUM_H_
/*
	MyNum类  存放单个数字元
	由于想要实现大数的运算  所以用int数组来存放数字(用了deque因为比vector更方便一点)
	模仿IEEE浮点数的思路 将数字转化为  0.xxxxx * 10^power
		其中xxxxx是"有效数字"  power是阶数		注意: 这里的"有效数字"不包括末尾的一些0
	deque<int> digits	//有效数字
	int power			//幂指数
	int sign			//符号  1正-1负  0表示零  -2 ERROR  2表示计算符 

	一些特殊量: 
		0:  digits={0} power=0 sign=0 即这里面的zero常量
		ERROR: digits={-1} power=0 sign=-2
		sign=2: 表示计算符  此时将digits临时变为char型数组 存放函数名称
				其中power的含义变为运算符优先级
				( -1  逻辑判断符0   +- 1    /*% 2   ^3   函数4
*/
#include <iostream>
#include <deque>
#include <string>
#include <cstring>
#include <cmath>


#define PRECISION 35		//设定默认精度

using std::string;

const std::deque<int> ZERODEQUE(1);

const std::deque<string> errorreason({
		"禁止出现除以0!",
		"禁止出现0的0次幂!",
		"禁止出现负数的分数次幂!",
		"表达式非法!",
		"log函数不允许非正底数出现!"
	}
);

class MyNum{
public:
	//构造函数
	MyNum(const std::deque<int>& d=ZERODEQUE,int p=0,int s=0)
		:digits(d),power(p),sign(s){}
	MyNum(int i);
	MyNum(long long i);
	MyNum(double d);
	MyNum(std::string str);

	//拷贝构造拷贝赋值
	MyNum(const MyNum& other)
		:digits(other.digits), power(other.power),sign(other.sign) {}
	MyNum& operator= (const MyNum& other) {
		this->digits = other.digits;
		this->power = other.power;
		this->sign = other.sign;
		return *this;
	}

	//析构
	~MyNum() {}

	//数学函数
	static MyNum getPI();	//求π
	//单目数学函数
	MyNum My_sqrt() const;
	MyNum My_sin() const; MyNum My_cos() const; MyNum My_tan() const;
	MyNum My_exp() const;
	MyNum My_fabs() const;
	MyNum My_neg() const; 
	MyNum My_log() const;
	//多目数学函数
		//见类外定义的非成员函数
	friend MyNum My_gcd(const MyNum& a, const MyNum& b);
	friend MyNum My_lcm(const MyNum& a, const MyNum& b);

	//转换函数
	double to_double() const;
	int to_int() const;
	long long to_longlong() const;
	std::string to_string() const;
	MyNum decimalpart() const;
	MyNum addError(int i)const;

	//运算符重载
	bool operator<(const MyNum& other)const;
	bool operator>(const MyNum& other)const;
	bool operator==(const MyNum& other)const;
	bool operator!=(const MyNum& other)const;
	bool operator<=(const MyNum& other)const;
	bool operator>=(const MyNum& other)const;
	MyNum operator+(const MyNum& other)const;
	MyNum operator-()const;
	MyNum operator-(const MyNum& other)const;
	MyNum operator*(const MyNum& other)const;
	MyNum operator/(const MyNum& other)const;
	MyNum operator^(const MyNum& other)const;
	MyNum power_int(int k)const;
	MyNum power_decimal(const MyNum& decimal)const;
	MyNum& operator+=(const MyNum& other);
	MyNum& operator-=(const MyNum& other);
	MyNum& operator*=(const MyNum& other);
	MyNum& operator/=(const MyNum& other);


	//显示
	void show() const;
	void setPrecision(int prec = PRECISION);


private:
	std::deque<int> digits;
	int power;
	int sign;

	friend class Method;
};


MyNum My_gcd(const MyNum& a, const MyNum& b);
MyNum My_lcm(const MyNum& a, const MyNum& b);

#endif

MyNum.cpp

#define _CRT_SECURE_NO_WARNINGS
#include "MyNum.h"
#include <iostream>
#include <cstdio>

#define ZERO_LIMIT 1e-6		//小于这个数字的浮点数 就判定为0
#define TAYLOR_LIMIT 50		//数学函数里面使用泰勒展开的数据最大值 因为数据过大泰勒展开将会耗时过久


extern const MyNum ZERO({ 0 }, 0, 0);
extern const MyNum ERROR({}, 0, -2);
extern const MyNum ONE(1);
//const MyNum PI(MyNum::getPI());
extern MyNum PI("3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679821480865132823066470938446");
				//运用getPI函数将会超时... 因此直接读入pi会更好
extern MyNum PIDOUBLE("6.2831853071795864769252867665590057683943387987502116419498891846156328125724179972560696506842341359642961730265646132941876892");


//工具函数

int getmin(int a, int b) { return a < b ? a : b; }
int getmax(int a, int b) { return a > b ? a : b; }
int inttodeque(std::deque<int>& digits, int i) {
	if (i == 0) {
		digits.push_back(0);
		return 0;
	}
	int retpower = 0;
	i = i > 0 ? i : -i;
	while (i) {
		digits.push_front(i % 10);
		i /= 10;
		retpower++;
	}
	return retpower;
}
int cmp_digits(std::deque<int>& a, std::deque<int>& b) {
	for (int i = 0; i < getmin(a.size(), b.size());i++) {
		if (a[i] > b[i]) return 1;
		if (a[i] < b[i]) return -1;
	}
	if (a.size() > b.size()) return 1;
	if (a.size() < b.size()) return -1;
	return 0;
}
int minus_digits(std::deque<int>& a, std::deque<int>& b) {
	//前提确保了a>b
	for (int i = 0; i < b.size(); i++) {
		if (i == a.size())
			a.push_back(0);
		a[i] -= b[i];
	}
	for (int i = a.size() - 1; i >= 1; i--) {
		while (a[i] < 0) {
			a[i - 1] --;
			a[i] += 10;
		}
	}
	int ret = 0;	//返回借位
	while (a[0] < 0) {
		ret++;
		a[0]+=10;
	}
	return ret;
}
string digittostring(std::deque<int>& digits) {		
	string ans;
	for (int i = 0; i < digits.size(); i++) {
		ans.push_back(char(digits[i]));
	}
	return ans;
}

//构造函数
MyNum::MyNum(int i) {
	*this = MyNum((long long)i);
}
MyNum::MyNum(long long i) {
	if (i == 0ll) {
		*this = ZERO;
		return;
	}
	int power = 0;
	std::deque<int> digits;
	int sign = 1;
	if (i < 0) sign = -1;
	i *= sign;
	while (i) {
		digits.push_front(i % 10);
		i /= 10;
		power++;
	}
	while (digits.back() == 0)
		digits.pop_back();
	this->digits = digits;
	this->power = power;
	this->sign = sign;
}
MyNum::MyNum(double d) {
	if (d < ZERO_LIMIT && d>-ZERO_LIMIT) {
		*this = ZERO;
		return;
	}
	this->sign = (d > 0) ? 1 : -1;
	d *= sign;
	int retpower = 0;
	if(int(d)!=0) 
		retpower = inttodeque(this->digits, int(d));
	bool ifFindDemicalPoint = retpower >= 0;	//是否找到小数点
	d -= int(d);	//只保留小数部分
	while (d > ZERO_LIMIT || d < -ZERO_LIMIT) {
		d *= 10;
		int integer = int(d);
		d -= integer;
		if (ifFindDemicalPoint)
			this->digits.push_back(integer);
		else {
			if (integer == 0) 
				retpower--;
			else 
				ifFindDemicalPoint = true;
		}
	}
	this->power = retpower;
	while (this->digits.back() == 0) 
		this->digits.pop_back();
	this->setPrecision();
	/*char str[1000];
	sprintf(str, "%lf", d);
	*this = MyNum(std::string(str));*/		//sprintf转换成字符串只有6位小数的精度
}
MyNum::MyNum(std::string str) {
	if (str.size() && (str[0] < '0' || str[0]>'9')) {
		if (!((str[0] == '-' || str[0] == '+') && str.size() > 1 && str[1] >= '0' && str[1] <= '9')) {
			this->sign = 2;
			if (str[0] >= 'a' && str[0] <= 'z' || str[0] >= 'A' && str[0] <= 'Z') 
				power = 4;
			else {
				if (str[0] == '(') power = -2;
				if (str[0] == '=' || str[0] == '<' || str[0] == '>') power = 0;
				if (str[0] == '+' || str[0] == '-') power = 1;
				if (str[0] == '*' || str[0] == '/') power = 2;
				if (str[0] == '^') power = 3;
				if (str[0] == '%') power = 5;
				if (str[0] == ',')	power = -1;
			}
			for (int i = 0; i < str.size(); i++) {
				this->digits.push_back(int(str[i]));
			}
			return;
		}
	}
	this->sign = 1;
	int i = 0;
	while (str[i] == ' ') i++;
	if (str[i] == '-') {
		this->sign = -1; i++;
	}
	if (str[i] == '+') i++;

	int retpower = 0;
	bool startdigits = false;	//开始有效数字
	int powerchange = 1;		//1表示每往后一格power++  -1就是小数情况
	int ifdecimal = false;
	bool ifmeetpoint = false;	//有无遇见小数点
	while (i < str.size()) {
		if (startdigits && str[i]>='0' && str[i]<='9') {
			this->digits.push_back(str[i] - '0');
			if (!ifmeetpoint) 
				retpower++;
		}
		else {
			if (str[i] >= '1' && str[i] <= '9') {
				startdigits = true;
				this->digits.push_back(str[i] - '0');
				if (!ifmeetpoint)
					retpower++;
				if (ifdecimal) {
					this->power = retpower;
				}
			}
			if (str[i] == '0' && ifdecimal) {
				retpower--;
			}
			if (str[i] == '.') {
				ifmeetpoint = true;
				if (!startdigits) {	//小数
					ifdecimal = true;
				}
				else {
					this->power = retpower;
				}
			}
		}
		i++;
	}
	while (!this->digits.empty() && this->digits.back() == 0)
		this->digits.pop_back();
	if (digits.empty()) {
		*this = ZERO;
	}
	this->power = retpower;
	this->setPrecision();
}


//数学函数
MyNum MyNum::getPI() {	
	MyNum ret;
	MyNum add(1);
	for (int i = 1; add.sign != 0; i+=2) {
		add = ONE / MyNum(i);
		if ((i / 2) % 2)
			ret -= add;
		else
			ret += add;
	}
	return ret*MyNum(4);
}
//单目数学函数
MyNum MyNum::My_sqrt() const {
	//return *this^MyNum(0.5);
	return MyNum(sqrt(this->to_double()));
}
MyNum MyNum::My_sin() const {
	if (this->sign == 0) return ZERO;

	MyNum cal(*this);
	while (cal < -PIDOUBLE)
		cal += PIDOUBLE;
	while (cal > PIDOUBLE) 
		cal -= PIDOUBLE;	//PI优化
	MyNum ret;
	MyNum add(-1);
	for (int i = 1; add.sign != 0; i++) {
		add = add * cal / MyNum(i);
		if (i % 2) {
			add.sign *= -1;
			ret += add;
		}
	}
	return ret;
}
MyNum MyNum::My_cos() const{ 
	if (this->sign == 0) return MyNum(1);

	MyNum cal(*this);
	while (cal < -PIDOUBLE)
		cal += PIDOUBLE;
	while (cal > PIDOUBLE)
		cal -= PIDOUBLE;	//PI优化
	MyNum ret(1);
	MyNum add(1);
	for (int i = 1; add.sign != 0; i++) {
		add = add * cal / MyNum(i);
		if (i % 2 == 0) {
			add.sign *= -1;
			ret += add;
		}
	}
	return ret;
}
MyNum MyNum::My_tan() const{ 
	return this->My_sin() / this->My_cos();
}
MyNum MyNum::My_exp() const { 
	if (this->sign == 0) return ONE;
	if(*this>MyNum(TAYLOR_LIMIT))
		return MyNum(exp(this->to_double()));
	MyNum ret;
	MyNum add(1);
	for (int i = 1; add.sign != 0; i++) {
		ret += add;
		add *= (*this) / MyNum(i);
	}
	return ret;
}
MyNum MyNum::My_fabs() const {
	return MyNum(this->digits, this->power, (this->sign<0)?-sign:sign);		//不能直接是1  考虑到0
}
MyNum MyNum::My_neg() const {
	return -(*this);		
}
MyNum MyNum::My_log() const{ 
	if (this->sign <= 0) {
		return this->addError(4);
	}
	return MyNum(log(this->to_double()));
}
//多目数学函数
MyNum My_gcd_recursion(MyNum& a, MyNum& b) {	//递归辗转相除
	while (a > b) {
		a -= b;
	}
	if (a == b) 
		return a;
	else 
		return My_gcd_recursion(b, a);
}
MyNum My_gcd(const MyNum& a,const MyNum& b) {
	if (a.sign != b.sign) return ERROR;
	MyNum aa(a.digits,a.power,1),bb(b.digits,b.power,1);
	MyNum ret(My_gcd_recursion(aa, bb));
	ret.sign = a.sign;
	return ret;
}
MyNum My_lcm(const MyNum& a,const MyNum& b) {
	return a * b / My_gcd(a, b);
}

//转换函数
double MyNum::to_double() const {
	double ret = 0;
	const std::deque<int>& digits=this->digits;
	for (int i = digits.size() - 1; i >= 0; i--) {	//化为类科学计数法 0.xxx
		ret += digits[i];
		ret /= 10;
	}
	if(this->power>0)
		for (int i = 0; i < this->power; i++)
			ret *= 10;
	else {
		for (int i = 0; i < -this->power; i++) {
			ret /= 10;
		}
	}
	return ret;
}
int MyNum::to_int() const {
	if (power <= 0) return 0;
	int ret = 0;
	for (int i = 0; i < power; i++) {
		ret *= 10;
		if(i<this->digits.size())
			ret += this->digits[i];
	}
	return ret * this->sign;
}
long long MyNum::to_longlong() const {
	if (power <= 0) return 0ll;
	long long ret = 0;
	for (int i = 0; i < power; i++) {
		ret *= 10;
		ret += long long(this->digits[i]);
	}
	return ret * this->sign;
}
std::string MyNum::to_string() const {
	if (sign == -2) return "ERROR";
	if (sign == 0) return "0";

	string ans;
	if (sign == -1) ans.push_back('-');
	int power = this->power;
	const std::deque<int>& digits = this->digits;
	if (power <= 0) {
		ans += "0.";
		while (power < 0) {
			ans.push_back('0');
		}
		for (int i = 0; i < digits.size(); i++) {
			ans.push_back('0' + digits[i]);
		}
	}
	else {
		int i;
		for (i = 0; i < power; i++) {
			if (i >= digits.size())
				ans.push_back('0');
			else
				ans.push_back('0' + digits[i]);
		}
		if (i < digits.size()) {
			ans.push_back('.');
			for (; i < digits.size(); i++) {
				ans.push_back(digits[i]);
			}
		}
	}
	return ans;
}
MyNum MyNum::decimalpart() const {
	if (this->power <= 0) return *this;
	MyNum ret(*this);
	while (ret.power) {
		ret.power--;
		if(!ret.digits.empty())
			ret.digits.pop_front();
	}
	if (ret.digits.empty()) 
		return ZERO;
	return ret;
}

MyNum MyNum::addError(int i)const {
	MyNum ret(ERROR);
	ret.digits.clear();
	if (this->sign == -2)
		ret = *this;
	for (int temp = ret.digits.size(); temp <= i; temp++) {
		ret.digits.push_back(0);
	}
	ret.digits[i] = 1;
	return ret;
}

//运算符重载
bool MyNum::operator<(const MyNum& other)const {
	if (this->sign < other.sign) return true;
	if (this->sign > other.sign) return false;
	//下面同号情况
	if (this->power < other.power) return sign == 1;	//绝对值小 判断是否正数
	if (this->power > other.power) return sign == -1;	//绝对值小 判断是否正数
	//同号 指数相同 比较有效数字
	int i;
	for (i = 0; i < getmin(this->digits.size(), other.digits.size()); i++) {
		if (this->digits[i] < other.digits[i]) return sign == 1;
		if (this->digits[i] > other.digits[i]) return sign == -1;
	}
	//有效数字重叠  判断哪个短
	if (this->digits.size() < other.digits.size()) return sign == 1;
	if (this->digits.size() > other.digits.size()) return sign == -1;
	return false;
}
bool MyNum::operator>(const MyNum& other)const {
	if (this->sign > other.sign) return true;
	if (this->sign < other.sign) return false;
	//下面同号情况
	if (this->power > other.power) return sign == 1;	//绝对值大 判断是否正数
	if (this->power < other.power) return sign == -1;
	//同号 指数相同 比较有效数字
	int i;
	for (i = 0; i < getmin(this->digits.size(), other.digits.size()); i++) {
		if (this->digits[i] > other.digits[i]) return sign == 1;
		if (this->digits[i] < other.digits[i]) return sign == -1;
	}
	//有效数字重叠  判断哪个长
	if (this->digits.size() > other.digits.size()) return sign == 1;
	if (this->digits.size() < other.digits.size()) return sign == -1;
	return false;
}
bool MyNum::operator==(const MyNum& other)const {
	return (this->digits == other.digits && this->sign == other.sign && this->power == other.power);
}
bool MyNum::operator!=(const MyNum& other)const {
	return !(this->operator==(other));
}
bool MyNum::operator<=(const MyNum& other)const {
	return (*this) < other || (*this) == other;
}
bool MyNum::operator>=(const MyNum& other)const {
	return (*this) > other || (*this) == other;
}
MyNum MyNum::operator+(const MyNum& other)const {
	//ERROR
	if ((*this).sign == -2 || other.sign == -2) {
		if ((*this).sign == -2 && other.sign == -2) {
			MyNum reterror(ERROR);
			for (int i = 0; i < this->digits.size(); i++) {
				if (this->digits[i]) {
					reterror = reterror.addError(i);
				}
			}
			for (int i = 0; i < other.digits.size(); i++) {
				if (other.digits[i]) {
					reterror = reterror.addError(i);
				}
			}
			return reterror;
		}
		if ((*this).sign == -2) return *this;
		if (other.sign == -2) return other;
	}
	//异号
	if (this->sign * other.sign == -1) {
		if (this->sign < 0) return other - (-(*this));
		else return (*this) - (-other);
	}

	//0
	if (this->sign == 0) return other;
	if (other.sign == 0) return *this;

	//同号
	//找到较大的那个数 用它加上较小的数 比较省事
	MyNum const *bigger,*smaller;
	if (this->My_fabs() >= other.My_fabs()) 
		{ bigger = this; smaller = &other; }
	else
		{ bigger = &other; smaller = this; }
	int retpower = bigger->power;
	int retsign = bigger->sign;
	//对齐小数点后逐位相加
	int diff = bigger->power - smaller->power;		//小数点偏差
	std::deque<int> retdigits = bigger->digits;
	int length = 0;
	while (length < diff) {
		while (length >= retdigits.size())
			retdigits.push_back(0);
		length++;
	}
	for (int temp = 0; temp < smaller->digits.size(); temp++) {
		while (length >= retdigits.size())
			retdigits.push_back(0);
		retdigits[length++] += smaller->digits[temp];
	}
	//从后往前逐位校准
	for (int temp = length - 1; temp >= 1; temp--) {
		retdigits[temp - 1] += retdigits[temp] / 10;
		retdigits[temp] %= 10;
	}
	if (retdigits[0] >= 10) {
		retdigits.push_front(retdigits[0] / 10);
		retdigits[1] %= 10;
		retpower++;
	}
	//末尾去0
	while (!retdigits.empty() && retdigits.back() == 0)
		retdigits.pop_back();
	if (retdigits.empty()) return ZERO;

	MyNum ans(retdigits, retpower, retsign);
	ans.setPrecision();
	return ans;
}
MyNum MyNum::operator-()const {
	return MyNum(this->digits, this->power, -(this->sign));
}
MyNum MyNum::operator-(const MyNum& other)const {
	//ERROR
	if ((*this).sign == -2 || other.sign == -2) return *this+other;

	//异号
	if (this->sign * other.sign == -1) {
		if (this->sign < 0) return -(other + (-(*this)));
		else return (*this) + (-other);
	}

	//0
	if (this->sign == 0) return -other;
	if (other.sign == 0) return -(*this);

	//同号
	if ((*this) == other) return ZERO;
	int retsign;
	//找到绝对值较大的那个数 用它减去较小的数 比较省事
	MyNum const *bigger, *smaller;
	if (this->My_fabs() > other.My_fabs()) 
		{ bigger = this; smaller = &other; retsign = bigger->sign; }
	else
		{ bigger = &other; smaller = this; retsign = -bigger->sign; }
	int retpower = bigger->power;
	//对齐小数点逐位相减
	int diff = bigger->power - smaller->power;		//小数点偏差
	std::deque<int> retdigits = bigger->digits;
	int length = 0;
	while (length < diff) {
		while (length >= retdigits.size())
			retdigits.push_back(0);
		length++;
	}
	for (int temp = 0; temp < smaller->digits.size(); temp++) {
		while (length >= retdigits.size())
			retdigits.push_back(0);
		retdigits[length++] -= smaller->digits[temp];
	}

	//从后往前逐位校准
	for (int temp = length - 1; temp >= 1; temp--) {
		while (retdigits[temp] < 0) {
			retdigits[temp - 1]--;
			retdigits[temp] += 10;
		}
	}
	//去头0 去尾0
	while (!retdigits.empty() && retdigits.front() == 0) {
		retdigits.pop_front();
		retpower--;
	}
	while (!retdigits.empty() && retdigits.back() == 0) 
		retdigits.pop_back();
	if (retdigits.empty()) return ZERO;

	MyNum ans(retdigits, retpower, retsign);
	ans.setPrecision();
	return ans;
}
MyNum MyNum::operator*(const MyNum& other)const {
	//ERROR
	if ((*this).sign == -2 || other.sign == -2) return *this + other;

	//0
	if (this->sign == 0) return ZERO;
	if (other.sign == 0) return ZERO;

	int retsign = this->sign * other.sign;
	int retpower = this->power + other.power;
	std::deque<int> retdigits;
	for (int i = 0; i < this->digits.size(); i++) {
		if (this->digits[i] == 0) continue;
		int temp = i;
		for (int j = 0; j < other.digits.size(); j++) {
			while (temp >= retdigits.size()) 
				retdigits.push_back(0);
			retdigits[temp++] += this->digits[i] * other.digits[j];
		}
	}
	for (int temp = retdigits.size() - 1; temp >= 1; temp--) {
		retdigits[temp - 1] += retdigits[temp] / 10;
		retdigits[temp] %= 10;
	}
	retpower--;
	while (retdigits.front() >= 10) {
		retdigits.push_front(retdigits[0] / 10);
		retdigits[1] %= 10;
		retpower++;
	}
	MyNum ans(retdigits, retpower, retsign);
	ans.setPrecision();
	return ans;
}
MyNum MyNum::operator/(const MyNum& other)const {
	//ERROR
	if ((*this).sign == -2 || other.sign == -2) return *this + other;

	//除0
	if (other.sign == 0) {
		return ERROR.addError(0);
	}
	if (this->sign == 0) {
		return ZERO;
	}

	if (other == MyNum(1)) return *this;
	if (other == MyNum(-1)) return -*this;
	int retsign = this->sign * other.sign;
	int retpower = this->power - other.power;
	std::deque<int> a = this->digits;
	std::deque<int> b = other.digits;

	std::deque<int> ans;
	for (int temp = 0; temp <= PRECISION + retsign; temp++) {
		int next = 0;
		while (cmp_digits(a, b) > 0) {
			minus_digits(a, b);
			next++;
		}
		if (cmp_digits(a, b) == 0) {
			next++; 
			ans.push_back(next);
			break;
		}
		ans.push_back(next);
		b.push_front(0);
	}

	while (!ans.empty() && ans.front() == 0) {
		ans.pop_front();
		retpower--;
	}
	while (!ans.empty() && ans.back() == 0) {
		ans.pop_back();
	}
	retpower++;
	MyNum ret(ans, retpower, retsign);
	ret.setPrecision();
	return ret;
}
MyNum MyNum::operator^(const MyNum& other) const{
	//ERROR
	if ((*this).sign == -2 || other.sign == -2) return *this + other;

	MyNum b(other);
	if (b.sign == 0) return (this->sign == 0) ? ERROR.addError(1) : ZERO;
	if (this->sign == 0) return (b.sign > 0) ? ZERO : ERROR.addError(0);
	//仅排除了底数为零的情况 不保证幂指数的整数部分和小数部分不为0
	if (b.sign > 0)
		return power_int(other.to_int()) * (power_decimal(other.decimalpart()));	//整数部分快速幂 小数部分泰勒展开
	else
		return MyNum(1) / (power_int(-other.to_int()) * (power_decimal(-other.decimalpart())));
}
MyNum MyNum::power_int(int k)const {	
	if (k == 0) return MyNum(1);
	if (k < 0) return MyNum(1) / ((*this) ^ (-k));
	if (*this == MyNum(1)) return MyNum(1);
	
	//快速幂部分
	MyNum ans(1);
	MyNum bottom(*this);
	while (k) {
		if (k & 1) {
			ans *= bottom;
		}
		bottom *= bottom;
		k >>= 1;
	}
	ans.setPrecision();
	return ans;
}
MyNum MyNum::power_decimal(const MyNum& decimal) const{
	//ERROR
	if ((*this).sign == -2) return *this;
	
	if (decimal.sign == 0) return MyNum(1);
	if (this->sign < 0)	//底数负数 不可进行小数幂
		return ERROR.addError(2);

	MyNum mult = this->My_log() * decimal;	//泰勒展开每一项要乘的东西
	MyNum add(1);
	MyNum ret;
	for (int i = 1; add.sign != 0; i++) {
		ret += add;
		add = add * mult / MyNum(i);
	}
	return ret;
}
MyNum& MyNum::operator+=(const MyNum& other) {
	*this = *this + other;
	return *this;
}
MyNum& MyNum::operator-=(const MyNum& other) {
	*this = *this - other;
	return *this;
}
MyNum& MyNum::operator*=(const MyNum& other) {
	*this = *this * other;
	return *this;
}
MyNum& MyNum::operator/=(const MyNum& other) {
	*this = *this / other;
	return *this;
}



//显示
void MyNum::show() const {
	if (this->sign==-2) {
		printf("ERROR\n");
		for (int i = 0; i < digits.size(); i++) {
			if (digits[i])
				std::cout << errorreason[i] << std::endl;
		}
		return;
	}
	if (sign == 2) {
		if (digits[0] == ',') return;
		for (int i = 0; i < digits.size(); i++)
			printf("%c", char(digits[i]));
		return;
	}
	int power = this->power;
	int sign = this->sign;
	std::deque<int> digits = this->digits;
	if (sign == -1) printf("-");
	if (sign == 0) { printf("0"); return; }

	if (power <= 0) {
		printf("0.");
		while (power < 0) {
			printf("0"); power++;
		}
		power--;
	}
	for (int i = 0; i < digits.size(); i++) {
		if (power-- == 0) {
			printf(".");
		}
		printf("%d", digits[i]);
	}
	while (power > 0) {
		printf("0");
		power--;
	}
}
void MyNum::setPrecision(int prec) {
	int decimalpart = this->digits.size() - this->power;	//小数部分长度
	if (decimalpart <= PRECISION) 
		return;
	for (int i = 0; i < decimalpart - prec - 1; i++)
		if(!this->digits.empty())
			this->digits.pop_back();
	int diff = 0;	//四舍五入的偏差值
	if (!this->digits.empty()) {
		if (this->digits.back() >= 5) {
			diff = 1;	//进1
		}
		this->digits.pop_back();
	}
	if (this->digits.empty()) {
		*this = ZERO;
	}
	else {
		int i = this->digits.size() - 1;
		while (diff) {
			if (i == -1) break;
			this->digits[i] += diff;
			diff = this->digits[i] / 10;
			this->digits[i] %= 10;
			i--;
		}
		if (i == -1 && diff) {
			this->power++;
			this->digits.front() %= 10;
			this->digits.push_front(1);
		}
	}
}

Method.h

#ifndef _METHOD_H_

#define _METHOD_H_
/*
	Method类  存放计算式的原式 后缀表达式 以及 计算答案

	string calstr			//计算的原式
	std::deque<MyNum> rpn	//ReversePolishNotation 逆波兰表示法/后缀表达式
	int type				//0 计算式  1 逻辑条件式
	MyNum ans				//计算结果

*/
#include <iostream>
#include <cmath>
#include <string>

#include "MyNum.h"

using std::string;

extern int SHOW_PRECISION;

class Method{
public:
	//构造
	Method(string s,int t=0)
		:type(t){
		input(s);
		changetorpn();
		calculate();
	}

	//操作函数
	void input(std::string str);		///输入 将字符串中计算部分提取出来
	void opstackpushback(std::deque<MyNum>& opstack, MyNum op);	//计算符号入栈
	void changetorpn();	//将计算式转换为后缀表达式
	MyNum calculate();	//计算

	//显示
	void showCalstr();
	void showRPN();
	void showAns(int showprecision=SHOW_PRECISION);
	void show(int type = 0);

private:
	string calstr;
	std::deque<MyNum> rpn;
	int type;
	MyNum ans;
};

#endif

Method.cpp

#include "Method.h"

extern const MyNum ZERO;
extern const MyNum ERROR;
extern const MyNum ONE;

//操作函数
void Method::input(std::string str) {		//输入 将字符串中计算部分提取出来
	for (int i = 0; i < str.size(); i++) {
		if (str[i] != ' ')
			calstr.push_back(str[i]);
	}
}

void Method::opstackpushback(std::deque<MyNum>& opstack,MyNum op) {
	while (!opstack.empty() && opstack.back().power >= op.power) {
		rpn.push_back(opstack.back());
		opstack.pop_back();
	}
	opstack.push_back(op);
}

bool judgeop(char c) {		//判断是否为函数型op
	if ((c < 'a' || c>'z') && (c < 'A' || c>'Z')) return false;
	else return true;
	return false;
}

void Method::changetorpn() {
	this->rpn.clear();
	std::deque<MyNum>& retrpn = this->rpn;
	std::deque<MyNum> opstack;	//符号栈(用deque模拟栈)

	string nowdigits;
	string op;
	for (int i = 0; i < calstr.size(); i++) {
		if (calstr[i] == ' ') continue;
		if (calstr[i] == ',') {
			if (!nowdigits.empty()) {
				retrpn.push_back(MyNum(nowdigits));
				nowdigits.clear();
			}
			while (!opstack.empty() && opstack.back() != MyNum("(")) {
				retrpn.push_back(opstack.back());
				opstack.pop_back();
			}
			//opstack.pop_back();
			continue;
		}
		if (calstr[i] == '-' ) {
			if (!(!nowdigits.empty() || i > 0 && calstr[i - 1] == ')')) {	//'-'是负号不是减号
				if (!op.empty()) {
					opstackpushback(opstack, MyNum(op));
					op.clear();
				}
				opstackpushback(opstack, MyNum("neg"));
				continue;
			}
		}
		if (calstr[i] >= '0' && calstr[i] <= '9' ||calstr[i]=='.') {
			nowdigits.push_back(calstr[i]);
			if (!op.empty()) {
				opstackpushback(opstack, MyNum(op));
				op.clear();
			}
			continue;
		}
		if (!nowdigits.empty()) {
			retrpn.push_back(MyNum(nowdigits));
			nowdigits.clear();
		}
		if (calstr[i] == '(') {
			if (!op.empty()) {
				opstackpushback(opstack, MyNum(op));
				op.clear();
			}
			opstack.push_back(MyNum("("));
			continue;
		}
		if (calstr[i] == ')') {
			if (!nowdigits.empty()) {
				retrpn.push_back(MyNum(nowdigits));
				nowdigits.clear();
			}
			while (!opstack.empty() && opstack.back() != MyNum("(")) {
				retrpn.push_back(opstack.back());
				opstack.pop_back();
			}
			if (opstack.empty()) {	//多出来个右括号
				ans = ERROR.addError(3);
				return;
			}
			opstack.pop_back();
			if (!opstack.empty() && opstack.back().power == 4) {		//函数类计算后面是紧紧跟随括号的 所以括号被弹出后 函数也必须紧跟着弹出
				retrpn.push_back(opstack.back());
				opstack.pop_back();
			}
			continue;
		}
		//以下情况皆为calstr[i]为计算符号
		if (i < calstr.size() && !judgeop(calstr[i])) {		//符号型计算
			if (!op.empty() && judgeop(op[0])) {	//op中装着函数型计算
				opstackpushback(opstack, MyNum(op));
				op.clear();
			}
			else if(!op.empty()){		//原先有符号 但是无法凑在一起
				string tempop(op); tempop.push_back(calstr[i]);
				if (tempop != "<=" && tempop != ">=" && tempop != "==" && tempop != "!=") {
					opstackpushback(opstack, MyNum(op));
					op.clear();
				}
			}
			op.push_back(calstr[i]);
			continue;
		}
		if (i < calstr.size() && judgeop(calstr[i])) {		//函数型计算
			if (!op.empty() && !judgeop(op[0])) {	//op中装着符号型计算
				opstackpushback(opstack, MyNum(op));
				op.clear();
			}
			op.push_back(calstr[i]);
			continue;
		}
	}
	
	if (!op.empty()) {
		opstackpushback(opstack, MyNum(op));
		op.clear();
	}
	if (!nowdigits.empty()) {
		retrpn.push_back(MyNum(nowdigits));
		nowdigits.clear();
	}
	while (!opstack.empty()) {
		if (opstack.back() == MyNum("(")) {
			ans = ERROR.addError(3);
			return;
		}
		retrpn.push_back(opstack.back());
		opstack.pop_back();
	}
}


MyNum Method::calculate() {		//计算
	if (ans.sign == -2)
		return ans;
	if (rpn.empty()) {
		ans = ZERO;
		return ZERO;
	}

	if (rpn.back().power == 0 && rpn.back().sign != 0) {	//逻辑判断式
		type = 1;
	}
	std::deque<MyNum> tempans;
	for (int i = 0; i < rpn.size(); i++) {
		if (rpn[i].sign != 2) {
			tempans.push_back(rpn[i]);
			continue;
		}
		//运算符
		if (rpn[i].power == 0) {
			MyNum b;
			if (tempans.empty()) { 
				ans = ERROR.addError(3);
				return ans; 
			}
			else {
				b = tempans.back(); tempans.pop_back();
			}
			MyNum a;
			if (tempans.empty()) {
				ans = ERROR.addError(3);
				return ans;
			}
			else {
				a = tempans.back(); tempans.pop_back();
			}
			if (rpn[i]==MyNum(">")) {
				tempans.push_back(MyNum(a > b));
			}
			if (rpn[i] == MyNum(">=")) {
				tempans.push_back(MyNum(a >= b));
			}
			if (rpn[i] == MyNum("<")) {
				tempans.push_back(MyNum(a < b));
			}
			if (rpn[i] == MyNum("<=")) {
				tempans.push_back(MyNum(a <= b));
			}
			if (rpn[i] == MyNum("==")) {
				tempans.push_back(MyNum(a == b));
			}
			if (rpn[i] == MyNum("!=")) {
				tempans.push_back(MyNum(a != b));
			}
		}
		if (rpn[i].power == 1) {
			if (rpn[i].digits[0] == '+') {
				MyNum b;
				if (tempans.empty()) {
					ans = ERROR.addError(3);
					return ans;
				}
				else {
					b = tempans.back(); tempans.pop_back();
				}
				MyNum a;
				if (tempans.empty()) {
					ans = ERROR.addError(3);
					return ans;
				}
				else {
					a = tempans.back(); tempans.pop_back();
				}
				tempans.push_back(a + b);
			}
			if (rpn[i].digits[0] == '-') {
				MyNum b;
				if (tempans.empty()) {
					ans = ERROR.addError(3);
					return ans;
				}
				else {
					b = tempans.back(); tempans.pop_back();
				}
				MyNum a;
				if (tempans.empty()) {
					ans = ERROR.addError(3);
					return ans;
				}
				else {
					a = tempans.back(); tempans.pop_back();
				}
				tempans.push_back(a - b);
			}
		}
		if (rpn[i].power == 2) {
			if (rpn[i].digits[0] == '*') {
				MyNum b;
				if (tempans.empty()) {
					ans = ERROR.addError(3);
					return ans;
				}
				else {
					b = tempans.back(); tempans.pop_back();
				}
				MyNum a;
				if (tempans.empty()) {
					ans = ERROR.addError(3);
					return ans;
				}
				else {
					a = tempans.back(); tempans.pop_back();
				}
				tempans.push_back(a * b);
			}
			if (rpn[i].digits[0] == '/') {
				MyNum b;
				if (tempans.empty()) {
					ans = ERROR.addError(3);
					return ans;
				}
				else {
					b = tempans.back(); tempans.pop_back();
				}
				MyNum a;
				if (tempans.empty()) {
					ans = ERROR.addError(3);
					return ans;
				}
				else {
					a = tempans.back(); tempans.pop_back();
				}
				tempans.push_back(a / b);
			}
		}
		if (rpn[i].power == 3) {
			if (rpn[i].digits[0] == '^') {
				MyNum b;
				if (tempans.empty()) {
					ans = ERROR.addError(3);
					return ans;
				}
				else {
					b = tempans.back(); tempans.pop_back();
				}
				MyNum a;
				if (tempans.empty()) {
					ans = ERROR.addError(3);
					return ans;
				}
				else {
					a = tempans.back(); tempans.pop_back();
				}
				tempans.push_back(a ^ b);
			}
		}
		if (rpn[i].power == 4) {
			if (rpn[i] == MyNum("sqrt")) {
				MyNum a;
				if (tempans.empty()) {
					ans = ERROR.addError(3);
					return ans;
				}
				else {
					a = tempans.back(); tempans.pop_back();
				}
				tempans.push_back(a.My_sqrt());
			}
			if (rpn[i] == MyNum("log")) {
				MyNum a;
				if (tempans.empty()) {
					ans = ERROR.addError(3);
					return ans;
				}
				else {
					a = tempans.back(); tempans.pop_back();
				}
				tempans.push_back(a.My_log());
			}
			if (rpn[i] == MyNum("sin")) {
				MyNum a;
				if (tempans.empty()) {
					ans = ERROR.addError(3);
					return ans;
				}
				else {
					a = tempans.back(); tempans.pop_back();
				}
				tempans.push_back(a.My_sin());
			}
			if (rpn[i] == MyNum("cos")) {
				MyNum a;
				if (tempans.empty()) {
					ans = ERROR.addError(3);
					return ans;
				}
				else {
					a = tempans.back(); tempans.pop_back();
				}
				tempans.push_back(a.My_cos());
			}
			if (rpn[i] == MyNum("tan")) {
				MyNum a;
				if (tempans.empty()) {
					ans = ERROR.addError(3);
					return ans;
				}
				else {
					a = tempans.back(); tempans.pop_back();
				}
				tempans.push_back(a.My_tan());
			}
			if (rpn[i] == MyNum("exp")) {
				MyNum a;
				if (tempans.empty()) {
					ans = ERROR.addError(3);
					return ans;
				}
				else {
					a = tempans.back(); tempans.pop_back();
				}
				tempans.push_back(a.My_exp());
			}
			if (rpn[i] == MyNum("fabs")) {
				MyNum a;
				if (tempans.empty()) {
					ans = ERROR.addError(3);
					return ans;
				}
				else {
					a = tempans.back(); tempans.pop_back();
				}
				tempans.push_back(a.My_fabs());
			}
			if (rpn[i] == MyNum("gcd")) {
				MyNum b;
				if (tempans.empty()) {
					ans = ERROR.addError(3);
					return ans;
				}
				else {
					b = tempans.back(); tempans.pop_back();
				}
				MyNum a;
				if (tempans.empty()) {
					ans = ERROR.addError(3);
					return ans;
				}
				else {
					a = tempans.back(); tempans.pop_back();
				}
				tempans.push_back(My_gcd(a,b));
			}
			if (rpn[i] == MyNum("lcm")) {
				MyNum b;
				if (tempans.empty()) {
					ans = ERROR.addError(3);
					return ans;
				}
				else {
					b = tempans.back(); tempans.pop_back();
				}
				MyNum a;
				if (tempans.empty()) {
					ans = ERROR.addError(3);
					return ans;
				}
				else {
					a = tempans.back(); tempans.pop_back();
				}
				tempans.push_back(My_lcm(a, b));
			}
			if (rpn[i] == MyNum("neg")) {
				MyNum a;
				if (tempans.empty()) {
					ans = ERROR.addError(3);
					return ans;
				}
				else {
					a = tempans.back(); tempans.pop_back();
				}
				tempans.push_back(a.My_neg());
			}
		}
		if (rpn[i].power == 5) {
			if (rpn[i].digits[0] == '%') {
				MyNum a;
				if (tempans.empty()) {
					ans = ERROR.addError(3);
					return ans;
				}
				else {
					a = tempans.back(); tempans.pop_back();
				}
				tempans.push_back(a / MyNum(100));
			}
		}
	}

	if (tempans.size() != 1) 
		ans = ERROR; 
	else ans = tempans[0];
	return ans;
}

//显示
void Method::showCalstr() {
	if (ans.sign==-2) {
		ERROR.show(); return;
	}
	std::cout << calstr;
}
void Method::showRPN() {
	if (ans.sign == -2) {
		ans.show(); return;
	}
	for (int i = 0; i < rpn.size(); i++) {
		if (rpn[i].digits[0] == ',') continue;
		rpn[i].show(); printf(" ");
	}
}

int getmin(int a, int b);
int getmax(int a, int b);

void Method::showAns(int showprecision) {
	if (ans.sign < -1 || ans.sign > 1) {		//非数字 直接show
		ans.show();
		return;
	}
	string tempans;
	if (ans.power < -showprecision) {		//过于小的小数 直接0
		ZERO.show();
		return;
	}
	if (ans.sign == -1)
		tempans.push_back('-');
	if (type == 0) {
		int decimalsize = ans.digits.size() - ans.power;
		if (decimalsize <= showprecision) {
			ans.show();
			return;
		}
		string bias("0.");
		for (int i = 0; i < showprecision - 1; i++) {
			bias.push_back('0');
		}
		bias.push_back('1');
		std::deque<int> newdigits;
		for (int i = 0; i < showprecision + ans.power; i++) {
			newdigits.push_back(ans.digits[i]);
		}
		MyNum newans(newdigits, ans.power, ans.sign);
		if (ans.digits[showprecision + ans.power] >= 5)
			newans += bias;
		newans.show();
		return;
	}
	else {
		if (ans == ZERO) printf("FALSE");
		else printf("TRUE");
	}
}

void Method::show(int showtype) {
	if (showtype == 0) {
		showAns();
		return;
	}
	printf("原式: "); showCalstr(); printf("\n");
	printf("后缀表达式: "); showRPN(); printf("\n");
	printf("ans= "); showAns(); printf("\n");
}

main.cpp

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <string>

#include "MyNum.h"	
#include "Method.h"

using namespace std;

int SHOW_PRECISION;	//输出精度

std::ostream& operator<<(std::ostream& out, const MyNum& num) {
	num.show();
	return out;
}



std::deque<Method> history;		//计算历史记录
int showType;		//显示模式 0简洁模式 只有答案    1详细模式  有原式,后缀表达式,答案  

void showHistory() {
	for (auto i : history) {
		i.showCalstr(); printf("\nans = "); i.showAns(); cout << endl;
	}
}

int inputMethod() {
	char s[20000]{0};
	cout << ">> ";
	fflush(stdin);
	scanf("%[^\n]", s);
	string str(s);
	if (str.empty()) {
		getchar();
		return -1;
	}
	if (str == "end") 
		return 0;
	if (str == "history") {
		return 2;
	}
	if (str == "changeshowtype") {
		showType = 1 - showType;
		return 3;
	}
	if (str.size() > 12 && str.substr(0,12) == "setprecision") {
		int newprecision = 0;
		for (int i = 13; i < str.size(); i++) {
			newprecision *= 10;
			newprecision += str[i] - '0';
		}
		SHOW_PRECISION = newprecision;
		return 4;
	}
	Method m(str);
	history.push_back(m);
	return 1;
}

void Func() {
	int input = 1;
	while(true) {
		input = inputMethod();
		if (input == 0) break;
		if (input == 1) {
			history.back().show(showType);
			cout << endl;
			getchar();
		}
		if (input == 2) {
			printf("历史计算:\n");
			showHistory();
			getchar();
		}
		if (input == 3) {
			printf("已更改显示模式为:");
			if (showType == 1) printf("详细模式\n");
			else printf("简洁模式\n");
			getchar();
		}
		if (input == 4) {
			printf("输出小数精度已被设置为:%d位小数\n", SHOW_PRECISION);
			getchar();
		}
	}
}

void Welcome() {
	std::cout << R"(
			大数计算器  v1.0

在">>"后输入计算式 回车即可显现结果  输入"end"回车即结束程序
高级功能:
	* 输入"changeshowtype"即可更改显示模式
		- 简洁模式: 类似Matlab 回车只输出答案
		- 详细模式: 输出原式, 后缀表达式 以及答案
	* 输入"history"即可查看本次运行期间的计算记录
	* 输入"setprecision 数字"即可更改显示答案精度 最高不超过20位

)" << std::endl;
}

int main() {
	showType = 0;
	SHOW_PRECISION = 6;

	Welcome();
	Func();
	return 0;
}

/*
一些测试数据
(log(0.158^2.333)+exp((2/7)^-0.6))/sin(sqrt(6.66)/5.867^(1.28+0.276))
正确的ans应该为24.618012958794913524198906652290747191348807023896450870419


*/

写在后面 / 自己的一些牢骚

这一部分是大年三十那天晚上熬大夜把这个计算器整好之后有感而发写的东西 因为这个计算器其实是给别人做的一个课设 所以内容都是给那个兄弟说的话 不过既然是学习记录那就把这个也放进来吧 也算是心路历程

差不多就这么些要说的吧 写到这个时候已经是大年初一快凌晨五点了都 都这个点了就该到了emo时刻了 =w=

谁能想到我的年三十晚上全都在敲代码还有写报告呢 : )

好兄弟 完全没有怪罪你的意思哈 就是感叹一下 我其实是发自真心的想敲这个代码写这个报告的

这个寒假感觉过的挺压抑的其实

学习上就抛开等着开天辟地的下学期开学期末考试内容不谈 我一个寒假同时冒出来了两个大创项目 原来是大一上参加我朋友那里的一个项目 结果参加后讨论了十多天就没人说话了 一年多过去我以为这个项目早就黄了无了 结果这个b 寒假前忽然找到我告诉我 我要负责给他们的代码一个UI界面包装... 然后就得自学Qt 的全部内容...

但是我在大一下的时候 以为我那大创黄了 所以就跟我们专业的一个同学参加了另一个项目 这个寒假正好要开始猛学 所以又要自学python+机器学习+深度学习+图神经网络+图对比学习+etc. 只能说想想都头大..

学习上不谈了 好不容易从学校润回家 结果还是在家里疫情坐牢 虽然我阳了之后几乎没啥症状 但天天窝在家里不是打游戏就是硬着头皮学(因为几乎不怎么学得进去) 我精神感觉非常的颓靡只能说 十二月中旬的时候还有一个很大的挫折——有个奖学金 全校每个专业派了各自的年级第一 一共37个人 要选里面最优秀的15人发1w的奖学金 我为此准备了好久好久的答辩稿 满心期待结果最后还是落空 我终究还是配不上那更优秀的15人

说老实话 这个能给我打击这么大 想必还是我大一运气好期末考的比较好 拿到一些小成绩之后 飘了 所以这个1w奖学金我心里似乎就是认为我理应拿到它 但事实证明我并不配 直接给我这个膨胀的气球用尖刺给戳爆了

接着就很玉玉啊 每天学习效率也没有 玩的时候也没法好好玩因为有好多任务压在面前 当时看了孤独摇滚之后心血来潮买了把电吉他 也没时间好好弹 想出去打球 约同学也都是说自己阳了就不运动了 约不到人....

感觉每一天都阴沉沉的

不过之后刘轩哥找到我 说他有个兄弟有个课设让我帮忙做一下 能恰烂钱

我一听能恰烂钱就答应了 不过我自个儿本来就全是事情 还又答应一件"麻烦"事情 我也不知道我当时怎么想的

所以一开始帮好兄弟你做这个计算器的时候我 有点抓狂可以说 不但是没啥头绪无从下手 更还有个原因 就是我意识到我已经快一个月没碰过代码了 这种生疏感让我倍感恐惧

在去年的寒假 我刚接触C++ 刚自学了一些类的封装 我感觉 我草这个东西太有意思了 所以我当时决定自己用蹩脚的C++知识做一个小计算器 那几天我一点游戏都没碰 满心思都是在想怎么把这个计算器做出来 怎么给他添加更多的功能让他看起来很屌

几乎每天熬夜 一周之后我整出来了 那份从无到有创造属于自己的东西的过程确实太有意思 最后做出来的玩意儿能正常运行 实在太有成就感

等到大一下开学 系统的学了C++之后 我意识到我做的那计算器呃.... 高情商: 稚嫩

刘轩知道我做了那个计算器 所以你当时拜托他的时候他直接想到了哥们 问我可不可以 我把我那计算器对照了一下你们的要求 然后发现了有好多好多当时没发现的bug.... 难蚌 看来是得重头做了

这次一开始时候我那个对代码的生疏感一下子让我联想到了去年这个时候我对C++的着迷 然后再一看: 一年之后水平已经提高到了连代码都生疏的地步 然后又想起来自己寒假回家屁事没干 我直接想给自己一巴掌

硬着头皮从0搭建这个计算器 慢慢的 能有一些思路有一些头绪之后 我开始感觉到这有一些意思了 一年前熟悉的从无到有创造东西的感觉好像要回来了

本来只是奔着恰烂钱去的 从我开始找到这个乐趣之后 我就决定 哪怕我现在一堆破事 我也要把这个玩意儿给做出来

好兄弟 抱歉我在这里废了半天话 其实我就是想说 钱你就看着给吧 已经⑧关键了 这个计算器我塞进那么多功能 还有在年三十的凌晨敲这篇报告 更是为了我自个儿 也许是想给寒假前面一个多月的烂状态 稍微的整理一下 收拾一下

春节就是个辞旧迎新的节日嘛 所以我就用这个小计算器来辞个旧迎个新 希望接下来的2023会是顺利的一年吧

也祝好兄弟2023身体健康万事如意

希望这篇报告能帮到你嗷 如果有啥bug发现了就尽管找我 我来修 另外 我打算之后把这个项目给发到网上去 就当是个学习记录了 等你们交上去出完成绩我再发布吧 免得你们老师要查重 =w=

新年快乐!

​ XP

​ 2023.01.22

标签:return,大数,int,C++,sign,MyNum,计算器,const,other
From: https://www.cnblogs.com/xpxpxp/p/17191676.html

相关文章

  • C++ primer StrBlobPtr和StrBlobPtr
    1#include<iostream>2#include<stdio.h>3#include<string>4#include<memory>5#include<algorithm>6#include<initializer_list>7#inclu......
  • C++11 thread_local关键字
    这是一篇科普文--关于thread_local关键字首先,C++11之前并没有对并发进行任何的支持,C++11首次提供了以下的支持:语言核心定义了一个内存模型,保证当更改"被两个不同线程使......
  • 第一个C程序:如何在DevC++中编辑、编译和运行程序
    第一步:打开DevC++程序,选择文件——新建——源代码 第二步:保证输入法在英文状态下输入代码 第三步:保存文件,保存的时候选择.c文件格式 保存完之后*号消失 ......
  • C/C++课程设计题目及具体要求[2023-03-07]
    C/C++课程设计题目及具体要求[2023-03-07]三、课程设计题目及具体要求:(1)学生成绩管理程序要求:本程序用于教学单位(院/系)的学生成绩管理。要求程序能够实现学生信息......
  • [Effective] 1 让自己习惯 C++
    1让自己习惯C++条款01:视C++为一个语言联邦C++可以认为由4个次级语言组合而成:C是C++的语法基础;Object-orientedC++实现面向对象设计;TemplateC++实现泛型编......
  • Redis的介绍安装以及启动与使用还有五大数据类型
    目录一、介绍Redis1.详细介绍2.介绍总结二、安装启动以及运行Redis1.Redis的安装步骤2.Redis的启动方法3.图形化界面使用Redis4.pycharm使用Redis三、redis五大数据类......
  • 【C++】 结构化绑定“需要编译器标志“/std:c++17“
    问题  vs打开工程时报错:"结构化绑定"需要编译器标志"/std:c++17"  解决  ......
  • 蓝桥-13届-C++-B组-省赛-I题-李白打酒加强版
    最直接的做法,算是回溯吧:生成指定01数的序列挨个检查是否满足题意并计数能不能将生成和判断的过程统一呢?能不能记忆前面的序列呢瞄一眼题解,往动态规划的方向上靠#inc......
  • C++笔记-指针
    1.const指针和指向const的指针指向const的指针是在类型前加星号可以指向非const类型指针可以改变指向dereference不能改变值const指针是在类型后面加星号指针不可......
  • C++笔记-static本地变量
    static本地变量只能被本地看到,所以不同函数之间的static变量相同也没事,但是同一个函数调用多次会忽略后面的初始化。#include<iostream>voidmyStaticFunction(){......