大数科学计算器 C++
大数计算器目录
[*]大数计算器
[*]优点
[*]实现思路
[*]自定义类
[*]MyNum类★★★
[*]Method类
[*]具体函数实现
[*]MyNum类的函数
[*]构造 & 三大函数
[*]数学函数
[*]转换函数
[*]运算符重载
[*]显示函数
[*]Method类的函数
[*]构造函数
[*]操作函数
[*]显示函数
[*]主程序的功能 & 函数
[*]完整代码
[*]MyNum.h
[*]MyNum.cpp
[*]Method.h
[*]Method.cpp
[*]main.cpp
[*]写在后面 / 自己的一些牢骚
优点
[*]实现了任意大数字计算 突破了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标准下的浮点数表示法:
https://img2023.cnblogs.com/blog/2748445/202303/2748445-20230308125123510-1477795630.png
模仿的结果就是
将一个数字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自然指数函数 这怎么自定义实现呢?
方法:**泰勒展开!! ** 利用迭代 计算出泰勒展开中每项 然后就能得到自己想要精度的结果
这个详细的过程在后面讲到函数实现的时候再说吧 目前看一下成果:
https://img2023.cnblogs.com/blog/2748445/202303/2748445-20230308125217155-1267805084.png
准确率还是可以的
[*]转换函数
就是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' || str>'9')) { //string不是数字是个计算符号
if (!((str == '-' || str == '+') && str.size() > 1 && str >= '0' && str <= '9')) {
this->sign = 2;
if (str >= 'a' && str <= 'z' || str >= 'A' && str <= 'Z') //函数类型计算优先级4
power = 4;
else {
if (str == '(') power = -2;
if (str == '=' || str == '<' || str == '>') power = 0;
if (str == '+' || str == '-') power = 1;
if (str == '*' || str == '/') power = 2;
if (str == '^') power = 3;
if (str == '%') power = 5;
if (str == ',') power = -1; //根据不同的符号 确定各自的优先级
}
for (int i = 0; i < str.size(); i++) {
this->digits.push_back(int(str)); //将名字存进digits里面
}
return;
}
}
//下面就是str表示的是数字情况
this->sign = 1;
int i = 0;
while (str == ' ') i++;
if (str == '-') {
this->sign = -1; i++;
}
if (str == '+') 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>='0' && str<='9') {
this->digits.push_back(str - '0');
if (!ifmeetpoint)
retpower++;
}
else {
if (str >= '1' && str <= '9') {
startdigits = true;
this->digits.push_back(str - '0');
if (!ifmeetpoint)
retpower++;
if (ifdecimal) {
this->power = retpower;
}
}
if (str == '0' && ifdecimal) {
retpower--;
}
if (str == '.') {
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()
这个函数是求出π之后在三角函数里实现π优化用的实现方法是
https://img2023.cnblogs.com/blog/2748445/202303/2748445-20230308125249410-990272233.png
证明过程如下:
https://img2023.cnblogs.com/blog/2748445/202303/2748445-20230308125259540-658666250.png
源码:
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有精度的限制所以使用了泰勒展开进行迭代从而求出我们想要精度的结果
https://img2023.cnblogs.com/blog/2748445/202303/2748445-20230308125316105-1497211142.png
源码:
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()
首先 也是用了泰勒展开来求他们的值
https://img2023.cnblogs.com/blog/2748445/202303/2748445-20230308125326879-1452892813.png
除此以外 针对三角函数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
}效果拔群:
https://img2023.cnblogs.com/blog/2748445/202303/2748445-20230308125339714-514739577.png
[*]最大公约最小公倍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
}注意这里实现了一个神奇的功能:小数也可以求最大公约最小公倍!
https://img2023.cnblogs.com/blog/2748445/202303/2748445-20230308125349161-1055515292.png
转换函数
//转换函数
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()
给他一个参数ii指的是要添加哪一种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 = 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) {
reterror = reterror.addError(i);
}
}
for (int i = 0; i < other.digits.size(); i++) {
if (other.digits) {
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 += smaller->digits;
}
//从后往前逐位校准
for (int temp = length - 1; temp >= 1; temp--) {
retdigits += retdigits / 10;
retdigits %= 10;
}
if (retdigits >= 10) {
retdigits.push_front(retdigits / 10);
retdigits %= 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 -= smaller->digits;
}
//从后往前逐位校准
for (int temp = length - 1; temp >= 1; temp--) {
while (retdigits < 0) {
retdigits--;
retdigits += 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 == 0) continue;
int temp = i;
for (int j = 0; j < other.digits.size(); j++) {
while (temp >= retdigits.size())
retdigits.push_back(0);
retdigits += this->digits * other.digits;
}
}
for (int temp = retdigits.size() - 1; temp >= 1; temp--) {
retdigits += retdigits / 10;
retdigits %= 10;
}
retpower--;
while (retdigits.front() >= 10) {
retdigits.push_front(retdigits / 10);
retdigits %= 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)
std::cout << errorreason << std::endl;
}
return;
}
if (sign == 2) {
if (digits == ',') return;
for (int i = 0; i < digits.size(); i++)
printf("%c", char(digits));
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);
}
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 == ' ') 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 == ')')) { //'-'是负号不是减号 if (!op.empty()) { opstackpushback(opstack, MyNum(op)); op.clear(); } opstackpushback(opstack, MyNum("neg")); continue; } } if (calstr >= '0' && calstr12 && 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 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);#endifMyNum.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 > b) return 1; if (a < b) 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 -= b; } for (int i = a.size() - 1; i >= 1; i--) { while (a < 0) { a --; a += 10; } } int ret = 0; //返回借位 while (a < 0) { ret++; a+=10; } return ret;}string digittostring(std::deque& digits) { string ans; for (int i = 0; i < digits.size(); i++) { ans.push_back(char(digits)); } 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; sprintf(str, "%lf", d); *this = MyNum(std::string(str));*/ //sprintf转换成字符串只有6位小数的精度}MyNum::MyNum(std::string str) { if (str.size() && (str < '0' || str>'9')) { if (!((str == '-' || str == '+') && str.size() > 1 && str >= '0' && str sign = 2; if (str >= 'a' && str = 'A' && str digits.push_back(int(str)); } return; } } this->sign = 1; int i = 0; while (str == ' ') i++; if (str == '-') { this->sign = -1; i++; } if (str == '+') 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>='0' && strdigits.push_back(str - '0'); if (!ifmeetpoint) retpower++; } else { if (str >= '1' && str digits.push_back(str - '0'); if (!ifmeetpoint) retpower++; if (ifdecimal) { this->power = retpower; } } if (str == '0' && ifdecimal) { retpower--; } if (str == '.') { 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; 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; } return ret * this->sign;}long long MyNum::to_longlong() const { if (power digits); } 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); } if (i < digits.size()) { ans.push_back('.'); for (; i < digits.size(); i++) { ans.push_back(digits); } } } 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 < other.digits) return sign == 1; if (this->digits > other.digits) 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 > other.digits) return sign == 1; if (this->digits < other.digits) 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) { reterror = reterror.addError(i); } } for (int i = 0; i < other.digits.size(); i++) { if (other.digits) { 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 += smaller->digits; } //从后往前逐位校准 for (int temp = length - 1; temp >= 1; temp--) { retdigits += retdigits / 10; retdigits %= 10; } if (retdigits >= 10) { retdigits.push_front(retdigits / 10); retdigits %= 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) {
reterror = reterror.addError(i);
}
}
for (int i = 0; i < other.digits.size(); i++) {
if (other.digits) {
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 += smaller->digits;
}
//从后往前逐位校准
for (int temp = length - 1; temp >= 1; temp--) {
retdigits += retdigits / 10;
retdigits %= 10;
}
if (retdigits >= 10) {
retdigits.push_front(retdigits / 10);
retdigits %= 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 -= smaller->digits;
}
//从后往前逐位校准
for (int temp = length - 1; temp >= 1; temp--) {
while (retdigits < 0) {
retdigits--;
retdigits += 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 += diff; diff = this->digits / 10; this->digits %= 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)
std::cout << errorreason << std::endl;
}
return;
}
if (sign == 2) {
if (digits == ',') return;
for (int i = 0; i < digits.size(); i++)
printf("%c", char(digits));
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);
}
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;};#endifMethod.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 != ' ') 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 == ')')) { //'-'是负号不是减号 if (!op.empty()) { opstackpushback(opstack, MyNum(op)); op.clear(); } opstackpushback(opstack, MyNum("neg")); continue; } } if (calstr >= '0' && calstrb)); } 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位)"
页:
[1]