C++ dilinde hesap makinesi yapma

Bir örnek kodu göstereyim oradan değiştirip kullanın.
#include <iostream> using namespace std; int main() { char op; double num1, num2; cout << "İlk sayıyı girin: "; cin >> num1; cout << "İkinci sayıyı girin: "; cin >> num2; cout << "İşlemi seçin (+, -, *, /): "; cin >> op; switch(op) { case '+': cout << num1 << " + " << num2 << " = " << num1 + num2; break; case '-': cout << num1 << " - " << num2 << " = " << num1 - num2; break; case '*': cout << num1 << " * " << num2 << " = " << num1 * num2; break; case '/': if (num2 != 0) { cout << num1 << " / " << num2 << " = " << num1 / num2; } else { cout << "Hata: Sıfıra bölme hatası!"; } break; default: cout << "Geçersiz işlem!"; } return 0; }
 
Youtubede var ama istediğiniz nedir anlamadım kodlarmı birde c++ yeni başladıysanız yapısını öğrenin nasıl kod yapacağınızı değil...
 
Hesap makinesi nasıl yapılır?
Örnek kod:

#include <iostream>

using namespace std;

int main() {
int num1, num2, choice;
char op;

// Kullanıcıdan işlem türünü ve sayıları al
cout << "1. Toplama" << endl;
cout << "2. Çıkarma" << endl;
cout << "3. Çarpma" << endl;
cout << "4. Bölme" << endl;
cout << "Seçiminiz (1/2/3/4): ";
cin >> choice;

cout << "1. Sayıyı girin: ";
cin >> num1;

cout << "2. Sayıyı girin: ";
cin >> num2;

// Seçilen işleme göre hesaplama yap
switch (choice) {
case 1:
cout << num1 << " + " << num2 << " = " << num1 + num2 << endl;
break;
case 2:
cout << num1 << " - " << num2 << " = " << num1 - num2 << endl;
break;
case 3:
cout << num1 << " * " << num2 << " = " << num1 * num2 << endl;
break;
case 4:
cout << num1 << " / " << num2 << " = " << (float)num1 / num2 << endl;
break;
default:
cout << "Yanlış seçim!" << endl;
}

return 0;
}

 
Atacağım kod ama muhtemelen bir yerlerde hata yapmış olabilirim, 1-2 saat civarında yazdım, gözden kaçmış bir şey olabilir. Tüm durumları test etmediğim için de kaçan bir şey var mı bilmiyorum ama parse ve calculation doğru çalışıyor test ettiğim durumlar için.

Shunting yard güzel bir algoritma. Infix notasyonunda eşitlik veriyorsun, postfix notasyonu üretiyor. Sonrasında calculate fonksiyonu da verilmiş postfix'ten hesaplamayı yapıyor. Postfix'ten hesaplıyoruz çünkü infix'te işlem önceliğini tespit etmek zor.

Shunting yard algoritması kullanan hesap makinesi;
C++:
#include <cctype>
#include <cmath>
#include <format>
#include <iostream>
#include <limits.h>
#include <map>
#include <ostream>
#include <stack>
#include <stdexcept>
#include <string>
#include <type_traits>

/* Basic calculator using shunting yard algorithm. */
template <typename T> struct Calculator {
  private:
  // FOR TEST PURPOSES! The below instance, will allow us to pass a function pointer to a test function.
  static inline Calculator<T> *instance; 

  using type = T;
  std::string postfix;
  std::map<char, std::pair<unsigned int, char>> op_prec;

  type stringtotype(std::string str) {
    if constexpr(std::is_same<type, int>::value) {
      return std::stoi(str);
    }
    if constexpr(std::is_same<type, float>::value) {
      return std::stof(str);
    }
    if constexpr(std::is_same<type, double>::value) {
      return std::stod(str);
    }
  }

  int prec_comp(char operator1, char operator2) {
    if (op_prec[operator1].first <= op_prec[operator2].first) {
      return -1;
    }
    if (op_prec[operator1].first == op_prec[operator2].first) {
      return 0;
    }
    if(operator1 == '(' or operator2 == '(' or operator1 == ')' or operator2 == ')') {
      return -2;
    }
    return 1;
  }

  bool isOperator(char iteration) {
    for (auto it = op_prec.begin(); it != op_prec.end(); it++) {
      if (iteration == it->first) {
        return true;
      }
    }
    return false;
  }

  type docalculate(char op, type operand1, type operand2) {
    if (op == '+') {
      return operand1 + operand2;
    }
    if (op == '-') {
      return operand1 - operand2;
    }
    if (op == '*') {
      return operand1 * operand2;
    }
    if (op == '/') {
      if (operand2 == 0) {
        return 0;
      }
      return operand1 / operand2;
    }
    if (op == '^') {
      return std::pow<type, type>(operand1, operand2);
    }
    return INT_MIN;
  }

  /* Parse the given string to postfix notation. */
  std::string parse(std::string eq) {
    std::stack<char> operatorStack;
    std::stack<char> auxStack;
    
    std::string number;
    std::string output;

    /* Loop through the given infix. */
    for (auto it = eq.begin(); it != eq.end(); it++) {
      char ch = *it;
      // if given character is a whitespace, skip it. we don't need to deal with it.
      if (ch == ' ')
        continue;

      // if given character is a digit, that means it's an operand, add it to output with a hashtag.
      // we add a hashtag because for being able to know what is the operand.
      // if postfix was "353+", we wouldn't be sure if calculation will be 35 + 3 or 3 + 53.
      // to prevent this, we add a seperation token (hashtag in this case) so "35#3#+" means 35 + 3 to
      // calculate function.
      if (std::isdigit(ch)) {
        output += ch;
        number += ch;
        if(it == (eq.end() - 1)) {
          output += '#';
        }
        continue;
      }

      // if we hit an operator that means we reached the end of the number, adding hashtag to output will seperate
      // the operand from other operands.
      if(!number.empty()) {
        output += '#';
        number.clear();
      }
      // check for parantheses opening
      if (ch == '(') {
        operatorStack.push(ch);
        continue;
      }
      // if parantheses is closing, pop it to output
      // parantheses has higher precedence.
      if (ch == ')') {
        while (!operatorStack.empty() && operatorStack.top() != '(') {
          output += operatorStack.top();
          operatorStack.pop();
        }
        operatorStack.pop();
        continue;
      }

      // iteration hit an operator.
      if (isOperator(ch)) {
        // if given operator has higher precedence, pop everything till
        // the top operator has higher or equal precedence.
        while (!operatorStack.empty() && prec_comp(ch, operatorStack.top()) == -1  &&
                op_prec[ch].second == 'l') {
          output += operatorStack.top();
          operatorStack.pop();
        }
        // if operator has lower or equal precedence, the loop above won't
        // execute, so it's safe to add it to stack anyway.
        operatorStack.push(ch);
      }
    }

    // pop the operator stack till nothing remains.
    while (!operatorStack.empty()) {
      if (operatorStack.top() == '(') {
        throw std::invalid_argument("Given expression is invalid.");
      }
      output += operatorStack.top();
      operatorStack.pop();
    }
    postfix = output;
    return output;
  }

public:
  Calculator() {
    // for testing, this needs to be set.
    instance = this;
    // Check if given type is int, float or double. if not, don't allow compiling.
    constexpr bool allowed_Types =
        (std::is_same<type, int>::value || std::is_same<type, double>::value ||
         std::is_same<type, float>::value);
    static_assert(allowed_Types,
                  "You can'type use any non-numerical or unsigned types.");
    
    // set map for operators.
    op_prec['+'] = {2, 'l'};
    op_prec['-'] = {2, 'l'};
    op_prec['*'] = {3, 'l'};
    op_prec['/'] = {3, 'l'};
    op_prec['^'] = {4, 'r'};
  }
  std::string Parse(std::string eq) {
    return this->parse(eq);
  }

  static std::string ParseTest(std::string eq) {
    return instance->parse(eq);
  }

  type Calculate() {
  std::stack<type> operandStack;
  std::string number;
    for(auto token : postfix) {
      if(token == '#') {
        operandStack.push(stringtotype(number));
        number.clear();
        continue;
      }
      if(isOperator(token) && operandStack.size() >= 2) {
        type op1 = operandStack.top();
        operandStack.pop();
        type op2 = operandStack.top();
        operandStack.pop();
        type result = docalculate(token, op2, op1);
        operandStack.push(result);
        continue;
      }
      number += token;
    }
    return operandStack.top();
  }

  type Calculate(std::string eq) {
    Parse(eq);
    return Calculate();
  }

  static type CalculateTest(std::string eq) {
    return instance->Calculate(eq);
  }
};

template <class Ty> void GetInput(Ty &item, const std::string &Message = "") {
  std::cout << Message;
  std::cin >> item;
}

enum TEST_RESULT {
  SUCCESS,
  FAIL
};

template <class CaseType, class ResultType>
TEST_RESULT test(std::map<CaseType, ResultType> testCases, ResultType (&func)(CaseType)) {
  for(auto& [args, returnvalue] : testCases) {
    if(func(args) != returnvalue) {
      return TEST_RESULT::FAIL;
    }
  }
  return TEST_RESULT::SUCCESS;
}

template <>
struct std::formatter<TEST_RESULT> {
    constexpr auto parse(std::format_parse_context& ctx) {
        return ctx.begin();
    }

    auto format(const TEST_RESULT& obj, format_context& ctx) const {
        if(obj == TEST_RESULT::SUCCESS) {
          return std::format_to(ctx.out(), "{}", "SUCCESS");
        }
        return std::format_to(ctx.out(), "{}", "FAIL");
    }
};


std::ostream& operator<<(std::ostream& out, TEST_RESULT obj) {
  out << std::format("{}", obj);
  return out;
}

int main() {
  // You have to create an object first to test the functions.
  Calculator<int> intcalc;
  Calculator<double> dcalc;
  std::map<std::string, double> calctestcases;

  calctestcases["3+5-(2+6)*3"] = -16;
  calctestcases["24 - 32 + 29 * 3 / (3 - 9)"] = -22.5;
  calctestcases["2^3"] = 8;
  calctestcases["2^3 + 10"] = 18;
  calctestcases["2 ^ 3 + (5 * 3) / 2"] = 15.5;

 
  std::cout << std::format("Calculate test: {}",  test(calctestcases, Calculator<double>::CalculateTest));
  return 0;
}
 
Atacağım kod ama muhtemelen bir yerlerde hata yapmış olabilirim, 1-2 saat civarında yazdım, gözden kaçmış bir şey olabilir. Tüm durumları test etmediğim için de kaçan bir şey var mı bilmiyorum ama parse ve calculation doğru çalışıyor test ettiğim durumlar için.

Shunting yard güzel bir algoritma. Infix notasyonunda eşitlik veriyorsun, postfix notasyonu üretiyor. Sonrasında calculate fonksiyonu da verilmiş postfix'ten hesaplamayı yapıyor. Postfix'ten hesaplıyoruz çünkü infix'te işlem önceliğini tespit etmek zor.

Shunting yard algoritması kullanan hesap makinesi;
C++:
#include <cctype>
#include <cmath>
#include <format>
#include <iostream>
#include <limits.h>
#include <map>
#include <ostream>
#include <stack>
#include <stdexcept>
#include <string>
#include <type_traits>

/* Basic calculator using shunting yard algorithm. */
template <typename T> struct Calculator {
  private:
  // FOR TEST PURPOSES! The below instance, will allow us to pass a function pointer to a test function.
  static inline Calculator<T> *instance;

  using type = T;
  std::string postfix;
  std::map<char, std::pair<unsigned int, char>> op_prec;

  type stringtotype(std::string str) {
    if constexpr(std::is_same<type, int>::value) {
      return std::stoi(str);
    }
    if constexpr(std::is_same<type, float>::value) {
      return std::stof(str);
    }
    if constexpr(std::is_same<type, double>::value) {
      return std::stod(str);
    }
  }

  int prec_comp(char operator1, char operator2) {
    if (op_prec[operator1].first <= op_prec[operator2].first) {
      return -1;
    }
    if (op_prec[operator1].first == op_prec[operator2].first) {
      return 0;
    }
    if(operator1 == '(' or operator2 == '(' or operator1 == ')' or operator2 == ')') {
      return -2;
    }
    return 1;
  }

  bool isOperator(char iteration) {
    for (auto it = op_prec.begin(); it != op_prec.end(); it++) {
      if (iteration == it->first) {
        return true;
      }
    }
    return false;
  }

  type docalculate(char op, type operand1, type operand2) {
    if (op == '+') {
      return operand1 + operand2;
    }
    if (op == '-') {
      return operand1 - operand2;
    }
    if (op == '*') {
      return operand1 * operand2;
    }
    if (op == '/') {
      if (operand2 == 0) {
        return 0;
      }
      return operand1 / operand2;
    }
    if (op == '^') {
      return std::pow<type, type>(operand1, operand2);
    }
    return INT_MIN;
  }

  /* Parse the given string to postfix notation. */
  std::string parse(std::string eq) {
    std::stack<char> operatorStack;
    std::stack<char> auxStack;
   
    std::string number;
    std::string output;

    /* Loop through the given infix. */
    for (auto it = eq.begin(); it != eq.end(); it++) {
      char ch = *it;
      // if given character is a whitespace, skip it. we don't need to deal with it.
      if (ch == ' ')
        continue;

      // if given character is a digit, that means it's an operand, add it to output with a hashtag.
      // we add a hashtag because for being able to know what is the operand.
      // if postfix was "353+", we wouldn't be sure if calculation will be 35 + 3 or 3 + 53.
      // to prevent this, we add a seperation token (hashtag in this case) so "35#3#+" means 35 + 3 to
      // calculate function.
      if (std::isdigit(ch)) {
        output += ch;
        number += ch;
        if(it == (eq.end() - 1)) {
          output += '#';
        }
        continue;
      }

      // if we hit an operator that means we reached the end of the number, adding hashtag to output will seperate
      // the operand from other operands.
      if(!number.empty()) {
        output += '#';
        number.clear();
      }
      // check for parantheses opening
      if (ch == '(') {
        operatorStack.push(ch);
        continue;
      }
      // if parantheses is closing, pop it to output
      // parantheses has higher precedence.
      if (ch == ')') {
        while (!operatorStack.empty() && operatorStack.top() != '(') {
          output += operatorStack.top();
          operatorStack.pop();
        }
        operatorStack.pop();
        continue;
      }

      // iteration hit an operator.
      if (isOperator(ch)) {
        // if given operator has higher precedence, pop everything till
        // the top operator has higher or equal precedence.
        while (!operatorStack.empty() && prec_comp(ch, operatorStack.top()) == -1  &&
                op_prec[ch].second == 'l') {
          output += operatorStack.top();
          operatorStack.pop();
        }
        // if operator has lower or equal precedence, the loop above won't
        // execute, so it's safe to add it to stack anyway.
        operatorStack.push(ch);
      }
    }

    // pop the operator stack till nothing remains.
    while (!operatorStack.empty()) {
      if (operatorStack.top() == '(') {
        throw std::invalid_argument("Given expression is invalid.");
      }
      output += operatorStack.top();
      operatorStack.pop();
    }
    postfix = output;
    return output;
  }

public:
  Calculator() {
    // for testing, this needs to be set.
    instance = this;
    // Check if given type is int, float or double. if not, don't allow compiling.
    constexpr bool allowed_Types =
        (std::is_same<type, int>::value || std::is_same<type, double>::value ||
         std::is_same<type, float>::value);
    static_assert(allowed_Types,
                  "You can'type use any non-numerical or unsigned types.");
   
    // set map for operators.
    op_prec['+'] = {2, 'l'};
    op_prec['-'] = {2, 'l'};
    op_prec['*'] = {3, 'l'};
    op_prec['/'] = {3, 'l'};
    op_prec['^'] = {4, 'r'};
  }
  std::string Parse(std::string eq) {
    return this->parse(eq);
  }

  static std::string ParseTest(std::string eq) {
    return instance->parse(eq);
  }

  type Calculate() {
  std::stack<type> operandStack;
  std::string number;
    for(auto token : postfix) {
      if(token == '#') {
        operandStack.push(stringtotype(number));
        number.clear();
        continue;
      }
      if(isOperator(token) && operandStack.size() >= 2) {
        type op1 = operandStack.top();
        operandStack.pop();
        type op2 = operandStack.top();
        operandStack.pop();
        type result = docalculate(token, op2, op1);
        operandStack.push(result);
        continue;
      }
      number += token;
    }
    return operandStack.top();
  }

  type Calculate(std::string eq) {
    Parse(eq);
    return Calculate();
  }

  static type CalculateTest(std::string eq) {
    return instance->Calculate(eq);
  }
};

template <class Ty> void GetInput(Ty &item, const std::string &Message = "") {
  std::cout << Message;
  std::cin >> item;
}

enum TEST_RESULT {
  SUCCESS,
  FAIL
};

template <class CaseType, class ResultType>
TEST_RESULT test(std::map<CaseType, ResultType> testCases, ResultType (&func)(CaseType)) {
  for(auto& [args, returnvalue] : testCases) {
    if(func(args) != returnvalue) {
      return TEST_RESULT::FAIL;
    }
  }
  return TEST_RESULT::SUCCESS;
}

template <>
struct std::formatter<TEST_RESULT> {
    constexpr auto parse(std::format_parse_context& ctx) {
        return ctx.begin();
    }

    auto format(const TEST_RESULT& obj, format_context& ctx) const {
        if(obj == TEST_RESULT::SUCCESS) {
          return std::format_to(ctx.out(), "{}", "SUCCESS");
        }
        return std::format_to(ctx.out(), "{}", "FAIL");
    }
};


std::ostream& operator<<(std::ostream& out, TEST_RESULT obj) {
  out << std::format("{}", obj);
  return out;
}

int main() {
  // You have to create an object first to test the functions.
  Calculator<int> intcalc;
  Calculator<double> dcalc;
  std::map<std::string, double> calctestcases;

  calctestcases["3+5-(2+6)*3"] = -16;
  calctestcases["24 - 32 + 29 * 3 / (3 - 9)"] = -22.5;
  calctestcases["2^3"] = 8;
  calctestcases["2^3 + 10"] = 18;
  calctestcases["2 ^ 3 + (5 * 3) / 2"] = 15.5;

 
  std::cout << std::format("Calculate test: {}",  test(calctestcases, Calculator<double>::CalculateTest));
  return 0;
}
Çalışmıyor.
 

Dosya Ekleri

  • Ekran Görüntüsü (1).png
    Ekran Görüntüsü (1).png
    27,6 KB · Görüntüleme: 13

Yeni konular

Geri
Yukarı