Calculator problems...

I will tell a story how one problem can lead to another.
I am making a calculator which could calculate infinite amount of numbers, just by the user inputing the operation (e.g. 1+2×5/6). But i find a problem in writing all the numbers and characters into an array of e.g. int num[] and char c[]. But how to write them into an array of unknown boundaries? Also, i tried to write all the line to string, but its even worse - it is very difficult to extract doubles, ints and chars from a string. Of course, it is possible to get 2 nums and char from cin, count them, then get the following char and num, and calculate with the previous, but this wouldnt calculate the multiplication and division first. But in arrays it is possible to count it first, and then do addition and subtraction. Then we wouln't get the 2+2×2=8 phenomena. But how to write them to arrays if it is unknown how many numbers there will be???
I think it is possible to make an arrays of lets say int num[1000] and char c[1000], then increase some int (i++) (e.g. 2+2+... each time char is written i++). Then, when the operation is over, the user presses enter, then 2 new arrays are created: int num_new[i+1] and char c_new [i], then copy info from first arrays to these new, and then do calculations.
But there is a new problem! Using conio.h which includes kbhit() and getch() is essential to check if enter is pressed, but conio.h is not recognized by many compilers, and i want my program to be cross platform. There is a solution -
1
2
3
4
5
6
7
#ifdef __WIN32__ 
#include <windows.h>

#elifdef __APPLE__
#include <somethingAppleNeeds>

//and so on, linux, android blah blah blah 

But thats a new problem - i dont know the specific code needed for those OSs to handle button presses, it requires further learning.

Thats a very good example how one problem can lead to another, especially if the programmer is average beginner, like me.
Last edited on
To make an array of unknown size, use a vector:

std::vector<int> myVector;

To add an element to your vector do this:
myVector.push_back(1);

and to iterate do this:
1
2
for (int i = 0; i < myVector.size(); ++i)
  cout << myVector[i];

or
1
2
for (std::vector<int>::iterator it = myVector.begin(); it != myVector.end(); ++it)
  cout << *it;


Then, you'll want to tokenize your equation... Read it in as one large string and figure out a way to separate the tokens.

I'd define my tokens like so:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Token
{
public: 
  enum eType
  {
    Operator,
    Number
  } type;

  enum eOperator
  {
    Add,
    Subtract,
    Multiply,
    Divide
  } oper;

  int number;

  Token(eOperator _oper) : type(Operator), oper(_oper) {}
  Token(int _number) : type(Number), number(number) {}
};

Then I'd tokenize:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
std::string equationToParse("1+2*5/6");
std::vector<Token> tokens;

for (int i = 0; i < equationToParse.size(); ++i) // For each character
{
  if (equationToParse[i] < '9' && equationToParse[i] > '0') // If the character is a number
    tokens.push_back(equationToParse[i] - '0'); // Convert to number and add to tokens
    // Note this doesn't support numbers > 9

  else if (equationToParse[i] == '*')
    tokens.push_back(Token::Multiply);
  else if (equationToParse[i] == '/')
    tokens.push_back(Token::Divide);
  else if (...)

}


Then you can iterate through the vector and reduce until you are done:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// Multiply and Divide first
for (int i = 0; i < tokens.size()-3; ++i)
{
  if (tokens[i+1].type == Token::Operator) // if it's an operator
  {
    if (tokens[i+1].oper == Token::Multiply) // and it's a multiply
    {
      tokens[i] = tokens[i] * tokens[i+2]; // replace this token with the product of this and the next number
      tokens.erase( tokens.begin()+i+1, tokens.begin()+i+2 );
    }
    else if (tokens[i].oper == Token::Divide )
    {
      // Same thing but for divide
    }
  }
}
// Add and subtract next
for (int i = 0...


There are certainly bugs in this (like missing support for integers larger than 9, and not checking for errors if you have two numbers or two operators beside each other). I'll let you work through all of that.
Last edited on
Wow, thanks for your detail answer! Even if it seems too complicated for me at first, because i havent done stuff with vectors before, but im sure i will find out how it works :) *sorry for my bad english, im lithuanian* :)
...i don't know the specific code needed for those OSs to handle button presses, it requires further learning.

No what it requires is a library that already has what you are looking for, check out QT. Trust me when I say that Cocoa can be terrifying to look at for a beginner.
Here's an example for a number pad GUI which is cross-platform and uses Qt. It wouldn't be hard to extend it for your needs.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#include <QDialog>
#include <QString>
#include <QLineEdit>
#include <QPushButton>

class NumPad : public QDialog
{
    Q_OBJECT
public:
    explicit NumPad(QWidget *parent = 0);
    void retranslateUi(QDialog *n);
    ~NumPad() {}
    int getInt()           { return m_string.toUInt();   }
    double getDouble()     { return m_string.toDouble(); }
private slots:
    void on_m_0_clicked()   { addChar('0');   }
    void on_m_1_clicked()   { addChar('1');   }
    void on_m_2_clicked()   { addChar('2');   }
    void on_m_3_clicked()   { addChar('3');   }
    void on_m_4_clicked()   { addChar('4');   }
    void on_m_5_clicked()   { addChar('5');   }
    void on_m_6_clicked()   { addChar('6');   }
    void on_m_7_clicked()   { addChar('7');   }
    void on_m_8_clicked()   { addChar('8');   }
    void on_m_9_clicked()   { addChar('9');   }
    void on_m_dot_clicked() { addChar('.');   }
    void on_m_ok_clicked()  { this->accept(); }
    void on_m_esc_clicked() { this->reject(); }
private:
    void addChar(char c)
    {
        m_string.append( c );
        m_display->setText(m_string);
    }

    QString m_string;
    QLineEdit   *m_display;
    QPushButton *m_0, *m_1, *m_2, *m_3, *m_4;
    QPushButton *m_5, *m_6, *m_7, *m_8, *m_9;
    QPushButton *m_dot, *m_ok, *m_esc;
};

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
#include "NumPad.h"

NumPad::NumPad(QWidget *parent) : QDialog(parent)
{
    if (this->objectName().isEmpty())
        this->setObjectName(QString::fromUtf8("NumPad"));
    this->resize(270, 300);

    m_0       = new QPushButton(this);
    m_1       = new QPushButton(this);
    m_2       = new QPushButton(this);
    m_3       = new QPushButton(this);
    m_4       = new QPushButton(this);
    m_5       = new QPushButton(this);
    m_6       = new QPushButton(this);
    m_7       = new QPushButton(this);
    m_8       = new QPushButton(this);
    m_9       = new QPushButton(this);
    m_dot     = new QPushButton(this);
    m_ok      = new QPushButton(this);
    m_esc     = new QPushButton(this);
    m_display = new QLineEdit  (this);

    m_0->setObjectName      (QString::fromUtf8("m_0"));
    m_1->setObjectName      (QString::fromUtf8("m_1"));
    m_2->setObjectName      (QString::fromUtf8("m_2"));
    m_3->setObjectName      (QString::fromUtf8("m_3"));
    m_4->setObjectName      (QString::fromUtf8("m_4"));
    m_5->setObjectName      (QString::fromUtf8("m_5"));
    m_6->setObjectName      (QString::fromUtf8("m_6"));
    m_7->setObjectName      (QString::fromUtf8("m_7"));
    m_8->setObjectName      (QString::fromUtf8("m_8"));
    m_9->setObjectName      (QString::fromUtf8("m_9"));
    m_ok->setObjectName     (QString::fromUtf8("m_ok"));
    m_dot->setObjectName    (QString::fromUtf8("m_dot"));
    m_esc->setObjectName    (QString::fromUtf8("m_esc"));
    m_display->setObjectName(QString::fromUtf8("m_display"));

    m_0->setGeometry(      QRect(20 , 240, 111, 51 ));
    m_1->setGeometry(      QRect(20 , 180, 51 , 51 ));
    m_2->setGeometry(      QRect(80 , 180, 51 , 51 ));
    m_3->setGeometry(      QRect(140, 180, 51 , 51 ));
    m_4->setGeometry(      QRect(20 , 120, 51 , 51 ));
    m_5->setGeometry(      QRect(80 , 120, 51 , 51 ));
    m_6->setGeometry(      QRect(140, 120, 51 , 51 ));
    m_7->setGeometry(      QRect(20 , 60 , 51 , 51 ));
    m_8->setGeometry(      QRect(80 , 60 , 51 , 51 ));
    m_9->setGeometry(      QRect(140, 60 , 51 , 51 ));
    m_ok->setGeometry(     QRect(200, 180, 51 , 111));
    m_dot->setGeometry(    QRect(140, 240, 51 , 51 ));
    m_esc->setGeometry(    QRect(200, 60 , 51 , 111));
    m_display->setGeometry(QRect(20 , 10 , 231, 41 ));

    retranslateUi(this);

    QMetaObject::connectSlotsByName(this);
}

void NumPad::retranslateUi(QDialog *n)
{
    n->setWindowTitle(QApplication::translate("NumPad", "Dialog", 0, QApplication::UnicodeUTF8));
    m_7->setText(  QApplication::translate("NumPad", "7", 0, QApplication::UnicodeUTF8));
    m_4->setText(  QApplication::translate("NumPad", "4", 0, QApplication::UnicodeUTF8));
    m_1->setText(  QApplication::translate("NumPad", "1", 0, QApplication::UnicodeUTF8));
    m_0->setText(  QApplication::translate("NumPad", "0", 0, QApplication::UnicodeUTF8));
    m_8->setText(  QApplication::translate("NumPad", "8", 0, QApplication::UnicodeUTF8));
    m_9->setText(  QApplication::translate("NumPad", "9", 0, QApplication::UnicodeUTF8));
    m_5->setText(  QApplication::translate("NumPad", "5", 0, QApplication::UnicodeUTF8));
    m_6->setText(  QApplication::translate("NumPad", "6", 0, QApplication::UnicodeUTF8));
    m_2->setText(  QApplication::translate("NumPad", "2", 0, QApplication::UnicodeUTF8));
    m_3->setText(  QApplication::translate("NumPad", "3", 0, QApplication::UnicodeUTF8));
    m_dot->setText(QApplication::translate("NumPad", ".", 0, QApplication::UnicodeUTF8));
    m_ok->setText( QApplication::translate("NumPad", "OK", 0, QApplication::UnicodeUTF8));
    m_esc->setText(QApplication::translate("NumPad", "ESC", 0, QApplication::UnicodeUTF8));
}
Last edited on
Topic archived. No new replies allowed.