大数计算器
目录
优点
- 实现了任意大数字计算 突破了longlong的-9223372036854775808到9223372036854775807的限制
- MyNum类的实现 尤其是内部数据成员的使用思路 是程序的关键也是核心部分 同样的数据结构能实现三种不同的功能
- MyNum类的存储方式受到了IEEE754标准下浮点数存储方式的启发
- 可以判断逻辑条件表达式 例如 1+1 >= 2 返回TRUE
- 计算的时候可自定义精度
- 输出可自定义精度 实现四舍五入 (这个跟上一点不重复 比如最后的答案是0.00079 我就可以输出0.0008 但这并不改变这个数字是0.00079的事实 只是输出变了)
- 计算过程中运用了后缀表达式法也就是逆波兰表达式法 实现了优先级计算
- 重载了+-*/^等基础运算符 大大提高代码可读性以及复用性
- 自定义函数有很多使用了自定义实现
- 对于三角函数 e^x函数 等一些函数 舍弃了库自带的函数 选择了利用泰勒展开数学迭代法进行自定义实现
- 针对三角函数不但有泰勒展开 还进行了π优化 (比如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 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() 这种数学的函数 其中很多函数舍弃掉了头文件中现有的函数 选择了自定义实现 目的就是提高精度!
有些函数例如三角函数 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 rpn: 这个very的重要 这个rpn是 Reverse 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+ERROR的情况 方便之后使用
-
减法跟加法大同小异 也是先对齐小数点然后逐位相减 然后从后往前处理 只是要注意一下处理完之后第一位被借没掉的情况 此时要变power
源码:- //运算符重载
- 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);
复制代码 *
还是列竖式 a的每一位数去乘b的每一位数 途中注意对齐问题 然后把所有的加起来 就是最终的结果 注意进位即可- 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 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;
- }
复制代码 ^
之前提过了 这些个运算里面最最有含金量的就是乘方运算 因为他实现了小数指数的计算
大致思路就是把小数指数 拆成整数和小数部分 其中整数部分操作的时候利用快速幂来进行优化 小数部分利用log()函数以及泰勒展开的技巧 迭代求出最后结果 两部分求出来之后最后乘到一起就是最终的结果
源码:- 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;
- }
复制代码 这个还是挺有含金量的
</ul>显示函数
- 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;
- }
复制代码 都没啥好说的
show()里面有个显示ERROR内容 大致就是有一个存放各个error名字的数组 如果这个MyNum是ERROR 那么就根据他的digits去看对应了哪一种的ERROR 然后输出就行
源码:- 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;
- }
复制代码 Method类的函数
构造函数
- //显示
- void show() const;
- void setPrecision(int prec = PRECISION);
复制代码 无要说的 就是正常的构造
操作函数
- 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--;
- }
- }
复制代码 重中之重就是一个把原计算式转换为后缀表达式的changetorpn() 还有一个计算函数calculate()
<ul>changetorpn()
简单思路: 通过遍历calstr计算式子字符串 把每个对应于数字的 对应于计算符号的 对应于计算函数的 全部一个个拆开来分成单独的MyNum 然后根据后缀表达式的转换规则 利用一个操作符单调栈opstack 按照操作符优先级的严格递增顺序进行入栈出栈 也就是严格单调栈 从而进行后缀表达式的转换
具体可以参见这篇文章: https://blog.csdn.net/a8425/article/details/119253258
源码:- void Method::changetorpn() { this->rpn.clear(); std::deque& retrpn = this->rpn; std::deque 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] 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 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; 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); 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;
- }private: std::deque 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 #include #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& 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& a, std::deque& 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& a, std::deque& 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& 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 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] sign = 2; if (str[0] >= 'a' && str[0] = 'A' && str[0] 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]digits.push_back(str[i] - '0'); if (!ifmeetpoint) retpower++; } else { if (str[i] >= '1' && str[i] 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->signsign 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& 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 digits[i]; } return ret * this->sign;}long long MyNum::to_longlong() const { if (power 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& digits = this->digits; if (power = 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 sign == -2) ret = *this; for (int temp = ret.digits.size(); temp 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;}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 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));}//运算符重载
- 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);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 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; 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;}//显示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 MyNum::setPrecision(int prec) { int decimalpart = this->digits.size() - this->power; //小数部分长度 if (decimalpart 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 rpn //ReversePolishNotation 逆波兰表示法/后缀表达式 int type //0 计算式 1 逻辑条件式 MyNum ans //计算结果*/#include #include #include #include "MyNum.h"using std::string;extern int SHOW_PRECISION;class Method{public: //显示
- void show() const;
- void setPrecision(int prec = PRECISION); 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 showCalstr(); void showRPN(); void showAns(int showprecision=SHOW_PRECISION); void show(int type = 0);private: string calstr; std::deque rpn; int type; MyNum ans;};#endif
复制代码 Method.cpp
[code]#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 != ' ') calstr.push_back(str); }}void Method::opstackpushback(std::deque& 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& retrpn = this->rpn; std::deque opstack; //符号栈(用deque模拟栈) string nowdigits; string op; for (int i = 0; i < calstr.size(); i++) { if (calstr == ' ') continue; if (calstr == ',') { 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 == '-' ) { if (!(!nowdigits.empty() || i > 0 && calstr[i - 1] == ')')) { //'-'是负号不是减号 if (!op.empty()) { opstackpushback(opstack, MyNum(op)); op.clear(); } opstackpushback(opstack, MyNum("neg")); continue; } } if (calstr >= '0' && calstr b)); } if (rpn == MyNum(">=")) { tempans.push_back(MyNum(a >= b)); } if (rpn == MyNum("> "; 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 - '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 "后输入计算式 回车即可显现结果 输入"end"回车即结束程序高级功能: * 输入"changeshowtype"即可更改显示模式 - 简洁模式: 类似Matlab 回车只输出答案 - 详细模式: 输出原式, 后缀表达式 以及答案 * 输入"history"即可查看本次运行期间的计算记录 * 输入"setprecision 数字"即可更改显示答案精度 最高不超过20位)" |