スタック・スタックポインターの理解のため、逆ポーランド記法電卓をプログラミング、そして動作確認してみました。恥ずかしながら、逆ポーランド記法(ここでは後置法のつもり)を正確に理解しておりません(エヘヘ笑)。ご指摘お待ちします。環境は以下です。
1.Windows 11 Pro
2.Visual Studio community 2022
3.「コンソールアプリ C++ Windows コンソール」として作成
非推奨のscnanf()
に警告が出てビルドできません。抑制するため、プロジェクトのプロパティを
のようにしておきます。
コードです。
#define _USE_MATH_DEFINES
#define _CRT_SECURE_NO_WARNING
#include <stdio.h>
#include <math.h>
#include <corecrt_math_defines.h>
constexpr auto stMax = 50;
int sp = 0;
bool sp_err = true;;
double stack[stMax];
int strcmp(const char* a, const char* b) {
while (*a == *b)
{
if (*a == '\0') return 0;
a++; b++;
}
return (*a - *b);
}
void printStack() {
printf("stack-----\n");
for (int i = sp - 1; i >= 0; i--)
printf("No.%2d : %f\n", i, stack[i]);
printf("----------\n");
}
int push(double n) {
if (sp < stMax) {
stack[sp++] = n;
return 0;
}
else return -1;
}
int pop(double* n) {
if (sp > 0) {
*n = stack[--sp];
return 0;
}
else {
sp_err = false;
printf("sp error. Under the Bottom.\n");
return -1;
}
}
double factorial(double n) {
double ret = 1.0;
for (double i = n; i > 0; i--) ret *= i;
return ret;
}
int main() {
while (sp_err) {
char buf[20];
printf(">");
scanf("%s", buf);
int sp_err = 0;
if (*buf == 'q') { break;// end
}
else if (*buf == 'a') { sp = 0; // All clear
}
else if (*buf == '+') {// input a, b, +
double b, a;
pop(&b); pop(&a);
push(a + b);
}
else if (*buf == '-') {// input a, b, -
double b, a;
pop(&b); pop(&a);
push(a - b);
}
else if (*buf == '*') {// input a, b, *
double b, a;
pop(&b); pop(&a);
push(a * b);
}
else if (*buf == '/') {// input a, b, /
double b, a;
pop(&b);
if (b == 0.0) {
printf("devide by 0 error.\n"); break;
}
pop(&a);
push(a / b);
}
else if (*buf == '^') {// power. a^b
double a, b; // input a, b, ^
pop(&b);pop(&a);
push(pow(a, b));
}
else if (*buf == '!') {// factorial n! valid to 22! (23! invalid.)
double a; // input x, !
pop(&a);
push(factorial(a));
}
else if (*buf == 'r') { // reciprocal 1/x
double a; // input x, r
pop(&a);
push(1.0/a);
}
else if (strcmp(buf, "sr") == 0) {// square root
double a; // input x, sr
pop(&a);
push(sqrt(a));
}
else if (strcmp(buf, "nr") == 0) { // n-th root
double a, b; // input n, x, nr
pop(&b);pop(&a);
push(pow(b,1.0/a));
}
else if (strcmp(buf, "pi") == 0) { // Pi=3.141593
push(M_PI);
}
else if (strcmp(buf, "ln") == 0) { // natural logarithm log(x)
double a; // input x, ln
pop(&a);
push(log(a));
}
else if (strcmp(buf, "log") == 0) {// log x (y)
double a, b; // input x, y, log
pop(&b);pop(&a);
push(log10(b) / log10(a));
}
else if (strcmp(buf, "exp") == 0) { // exp(x)
double a; // input x, exp
pop(&a);
push(exp(a));
}
else if (strcmp(buf, "sid") == 0) { // sin(x°)
double DtoR = M_PI / 180.0; // input x, sid
double a;
pop(&a);
push(sin(a * DtoR));
}
else if (strcmp(buf, "cod") == 0) { // cos(x°)
double DtoR = M_PI / 180.0; // input x, cod
double a;
pop(&a);
push(cos(a * DtoR));
}
else if (strcmp(buf, "tad") == 0) { // tan(x°)
double DtoR = M_PI / 180.0; // input x, tad
double a;
pop(&a);
push(tan(a * DtoR));
}
else if (strcmp(buf, "sin") == 0) { // sin(x radian)
double a; // input x, sin
pop(&a);
push(sin(a));
}
else if (strcmp(buf, "cos") == 0) { // cos(x radian)
double a; // input x, cos
pop(&a);
push(cos(a));
}
else if (strcmp(buf, "tan") == 0) { // tan(x radian)
double a; // input x, tan
pop(&a);
push(tan(a));
}
else{
/*
for (int i = 0; i < strlen(buf); i++) {
if (isdigit(buf[i])) {
printf("%c is digit.\n", i, buf[i]);
}
else {
printf("%c isn't digit.\n", i, buf[i]);
}
}
*/
double d = atof(buf);
push(d);
}
printStack();
}
}
これを「デバッグなしで開始」し、次のいくつかの例で確認してみました。数値とアルファベットを入力していきますが、各例では簡単のためリターンキーを「,」で代用して示します。
例1.円周率を「マチンの公式」で計算
【参考】https://manabitimes.jp/math/1229
キー入力(「,」リターンキーのこと。以下同じ)
16,5,r,3,5,3,^,*,r,-,*,4,239,/,-,
結果は「3.140597」で上記サイトと同じ。
例2.近似式
・(1+x)^(1/2)≒1+(1/2)x-(1/8)x^2
【参考】https://rikeilabo.com/approximate-expression
キー入力(x=0.1)
1,0.1,2,/,+,0.1,2,^,8,/,-,
結果は「1.048750」で上記サイトと同じ。
・tan(x)≒x+(1/3)x^3+(1/5)x^5
【参考】https://www.takamasu-lab.org/wp-content/uploads/2023/11/prec-tip02.pdf
キー入力(x=0.1)
0.1,3,r,0.1,3,^,*,5,r,0.1,5,^,*,+,+,
結果は「0.100335」。直接計算と全く同じ。
例3.対数計算
【参考】https://www.optics-words.com/math/exp/exponentiation_6.html
【4-1】問題(1) 累乗根を含む対数
キー入力
3,4,48,nr,log,3,2,log,-,
結果は「0.25」で上記サイトと同じ。
【4-2】問題(2) 底の変換公式の問題
キー入力
9,5,log,1,2,/,1,3,/,5,log,*,+,
結果は「0.0」で上記サイトと同じ。
【4-4】問題(4) 対数を指数にもつ値
キー入力
10,10,8,log,^,
結果は「8.0」で上記サイトと同じ。
例4.階乗の逆数和 無限に加えるとネイピアの数e
無限に続けるわけにはいかないので途中まで
キー入力
1,2,!,r,+,3,!,r,+,4,!,r,+,5,!,r,+,6,!,r,+,
結果は「1.718056」でした。
例5.三角関数の加法定理の確認
sin(30°+45°)=sin30°cos45°+cos30°sin45°
キー入力(右辺を入力)
30,sid,45,cod,*,30,cod,45,sid,*,+,
結果は「0.965926」となり、直接計算したものと同じでした。
以上のキー入力が逆ポーランド記法(後置法)として正しいのか、わかってないのがお恥ずかしいですが、計算ができているようだということです。最後までお付き合いいただきありがとうございました。