I cant get my invalid number detection to work, for my calculator

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
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
#include <iostream>
#include <cstdlib>
#include <time.h>
#include <math.h>
#include <errno.h> 

using namespace std;

const double MAXRANGE = pow(2.0, 16.0); // 65536
const double MINRANGE = -pow(2.0, 16.0);

bool validDouble(const char* argument)
{
	return atof(argument);
}

int main(int argc, char* argv[])
{
char z;

	bool isARealDouble = true;
	char b = ' ';

	if (argc == 3)
	{
		z = 0;
	}
	else if (argc == 4)
	{
		z = argv[3][0];
	}
	else if (argc < 3)
	{
		cout << "P" << endl;
		return 0;
	}
	else if (argc > 4)
	{
		cout << "P" << endl;
		return 0;
	}

	if (z == 'a' || z == 's' || z == 'm' || z == 'd' || z == 'p' || z == 0)
	{


		if (z == 0)
		{
			float h;
			float x = atof(argv[1]);
			float y = atof(argv[2]);
			if (x <= MINRANGE)
			{
				cout << "R" << endl;
				return 0;
			}
			else if (x >= MAXRANGE)
			{
				cout << "R" << endl;
				return 0;
			}
			if (y <= MINRANGE)
			{
				cout << "R" << endl;
				return 0;
			}
			else if (y >= MAXRANGE)
			{
				cout << "R" << endl;
				return 0;
			}

			for (int j = 0; j < argc; j++)
			{
				isARealDouble = true;

				for (int i = 0; i < strlen(argv[j]); i++)
				{
					 
					b = argv[j][i];

					if (isdigit(b) || b == '.')
					{	
						
						
						h = x + y;
						cout << h << endl;
						return 0;
						
					}
					else
					{
						
						cout << "X" << endl;
						return 0;
						
					}
						
				}
				
				
			}

		}

		switch (z)
		{
		case 'a':
		{
			float h;
			float x = atof(argv[1]);
			float y = atof(argv[2]);
			h = x + y;
			cout << h << endl;
			break;
		}
		case 's':
		{
			float h;
			float x = atof(argv[1]);
			float y = atof(argv[2]);
			h = x - y;
			cout << h << endl;
			break;
		}
		case 'm':
		{
			float h;
			float x = atof(argv[1]);
			float y = atof(argv[2]);
			h = x * y;
			cout << h << endl;
			break;
		}
		case 'd':
		{
			float h;
			float x = atof(argv[1]);
			float y = atof(argv[2]);
			if (y > 0)
			{
				h = x / y;
				cout << float(h) << endl;
				break;
			}
			else if (y <= 0)
			{
				cout << "M" << endl;
				break;
			}
		}
		case 'p':
		{
			float h;
			float x = atof(argv[1]);
			float y = atof(argv[2]);
			h = pow(x, y);
			cout << h << endl;
			break;
		}
		}
	}
	else
	{
		cout << "V" << endl;
		return 0;
	}

	return 0;
}
Last edited on
Im working on the first part the (z==0), im trying to get that working but trying to get it to work, im out of ideas
https://www.cplusplus.com/articles/jEywvCM9/
So we can better read your code.
sorry about that its fixed now
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
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
#include <iostream>
#include <cstdlib>
#include <time.h>
#include <math.h>
#include <errno.h>   

using namespace std;

const double MAXRANGE = pow(2.0, 16.0); // 65536
const double MINRANGE = -pow(2.0, 16.0);


int main(int argc, char* argv[])
{
	
	char z;
	bool isARealDouble = true;
	
	char b = ' ';
	
	if (argc == 3)
	{
		z = 0;
	}
	else if (argc == 4)
	{
		z = argv[3][0];
	}
	else if (argc < 3)
	{
		cout << "P" << endl;
		return 0;
	}
	else if (argc > 4)
	{
		cout << "P" << endl;
		return 0;
	}

	if ((z == 'a') || (z == 's') || (z == 'm') || (z == 'd') || (z == 'p') || (z == 0))
	{


		if (z == 0)
		{
			float h;
			float x = atof(argv[1]);
			float y = atof(argv[2]);
			if (x <= MINRANGE)
			{
				cout << "R" << endl;
				return 0;
			}
			else if (x >= MAXRANGE)
			{
				cout << "R" << endl;
				return 0;
			}
			if (y <= MINRANGE)
			{
				cout << "R" << endl;
				return 0;
			}
			else if (y >= MAXRANGE)
			{
				cout << "R" << endl;
				return 0;
			}

			for (int j = 1; j < argc; j++)
			{
				isARealDouble = true;

				for (int i = 0; i < strlen(argv[j]); i++)
				{
					 
					b = argv[j][i];
					cout << b << " ";
					if (isdigit(argv[j][i]) || argv[j][i] == '.')
					{	
						isARealDouble = true;
						if (isARealDouble == true)
							h = x + y;
							cout << h << endl;
							break;
						
					}
					else
					{
						isARealDouble = false;
						if (isARealDouble == false)
						{
						cout << "X" << endl;
						return 0;
						}
					}
						
				}
				
				
			}

		}

		switch (z)
		{
		case 'a':
		{
			float h;
			float x = atof(argv[1]);
			float y = atof(argv[2]);
			h = x + y;
			cout << h << endl;
			break;
		}
		case 's':
		{
			float h;
			float x = atof(argv[1]);
			float y = atof(argv[2]);
			h = x - y;
			cout << h << endl;
			break;
		}
		case 'm':
		{
			float h;
			float x = atof(argv[1]);
			float y = atof(argv[2]);
			h = x * y;
			cout << h << endl;
			break;
		}
		case 'd':
		{
			float h;
			float x = atof(argv[1]);
			float y = atof(argv[2]);
			if (y > 0)
			{
				h = x / y;
				cout << float(h) << endl;
				break;
			}
			else if (y <= 0)
			{
				cout << "M" << endl;
				break;
			}
		}
		case 'p':
		{
			float h;
			float x = atof(argv[1]);
			float y = atof(argv[2]);
			h = pow(x, y);
			cout << h << endl;
			break;
		}
		}
	}
	else
	{
		cout << "V" << endl;
		return 0;
	}

	return 0;
}




So I made some modification and fixed some errors, but I still have the problem of the code not completely going through the arguments, As the 2 argv[] variables I have it as 12a and 345, now when a character is at the end of the first argument or on the 2nd argument it doesnt work. I dont get why,
Last edited on
First of all, your 150 line main() should be broken down into functions.
Like for example, separate argument validation from argument use.

> for (int j = 0; j < argc; j++)
There's no point in checking argv[0], which is your program name, and unlikely to resemble a valid number.


> isARealDouble = false;
> if (isARealDouble == false)
So basically, all the time then.

Your test for isARealDouble should be outside your
for (int i = 0; i < strlen(argv[j]); i++)
Yes I realized about the isARealDouble, that was from different code I found but didnt understand I dont need it.
> float x = atof(argv[1]);
https://linux.die.net/man/3/strtod
The strto[fd] functions give you much better opportunities to validate and convert your input string.

1
2
3
4
5
6
7
8
9
10
11
12
char *endp = NULL;
errno = 0;
float x = strtof(argv[1],&endp);
if ( errno != 0 ) {
  // overflow or underflow
} else if ( *endp != '\0' ) {
  // trailing garbage on your input
} else if ( endp == argv[1] ) {
  // leading garbage on your input
} else {
  // should be good
}
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
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
#include <iostream>
#include <cstdlib>
#include <time.h>
#include <math.h>
#include <errno.h>   


using namespace std;

const double MAXRANGE = pow(2.0, 16.0); // 65536
const double MINRANGE = -pow(2.0, 16.0);

bool validDouble(const char* argument)
{
	return atof(argument);
}

int main(int argc, char* argv[])
{
	

	char z;

	bool isARealDouble = true;
	
	if (argc == 3)
	{
		z = 0;
	}
	else if (argc == 4)
	{
		z = argv[3][0];
	}
	else if (argc < 3)
	{
		cout << "P" << endl;
		return 0;
	}
	else if (argc > 4)
	{
		cout << "P" << endl;
		return 0;
	}

	if ((z == 'a') || (z == 's') || (z == 'm') || (z == 'd') || (z == 'p') || (z == 0))
	{


		if (z == 0)
		{
			float h;
			float x = atof(argv[1]);
			float y = atof(argv[2]);
			if (x <= MINRANGE)
			{
				cout << "R" << endl;
				return 0;
			}
			else if (x >= MAXRANGE)
			{
				cout << "R" << endl;
				return 0;
			}
			if (y <= MINRANGE)
			{
				cout << "R" << endl;
				return 0;
			}
			else if (y >= MAXRANGE)
			{
				cout << "R" << endl;
				return 0;
			}

			for (int i = 1; i < argc; i++)
			{
				isARealDouble = true;

				for (int j = 0; j < strlen(argv[i]); j++)
				{
				
					cout << argv[i][j] << " ";
					if (isdigit(argv[i][j]) || argv[i][j] == '.')
					{	
						isARealDouble = true;
											}
					else
					{
					isARealDouble = false;	
					cout << "X" << endl;
					return 0;
					}
						
				}
				
				
			}
			if (isARealDouble)
			{
				h = x + y;
				cout << h << endl;
				return 0;
			}
		}

		
	}
	

	return 0;
}


I got it working, Now the only problem I have is i cant stop the system from allowing an invalid number like 12.34.56, how would I go about that.
Last edited on
I already told you.
Hello WoodyW00,

I believe the root of your problem is a misunderstanding.

Whether it is "argv[1]"or "argv[2]" your brain sees "12.34.56" as an invalid number, but the "atof" or "srttof" functions do not.

Looking closer at the number you see "12 point 34 point 56" and the functions see "12 point 34 period 56".

Both "atof" and "srttof" will convert the string into a float until it finds a non digit character or new line whichever comes first. This makes "12.34" a valid number and the rest is ignored.

You could check the string for more than 1 (.) character and consider that to be an invalid number or take what can be converted.

Working with salem c's example I came up with this:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include<iostream>

int main(int argc, char* argv[])
{
    float num1{};

    char* str{ nullptr };

    std::cout << "\n " << argv[1] << '\n';

    num1 = strtof(argv[1], &str);

    std::cout << "\n " << num1 << '\n';

    return 0;
}


Which produces:

 12.34.56

 12.34



In your if statement
if ((z == 'a') || (z == 's') || (z == 'm') || (z == 'd') || (z == 'p') || (z == 0))

In the last or I am not sure what you are checking. I s it the number (0), a "\0" or the character '0'? It ma not make any difference, but it could. Also since it does not match the rest of the if statement it tends to make one stop and ponder what you are meaning and if it will work.

I have not tested your code yet, so I do not know how it actually works. Just a thought for now.

A last note "ctime", "cmath" and cerrno" are the better choice for these header files.

Andy
Good job on sticking with it and testing all these edge cases etc. Its worth your time to write the best common I/O checking you can and reuse it. Get it as bullet proof as you can and as efficient as you can and put it in your 'code I reuse all the time' folder. You WILL need this again.
Just to expound on @salem c's advice,

I routinely use a function similar to this:

1
2
3
4
5
6
7
8
9
10
bool parse_double(std::string_view s, double& d)
{
  char*       parse_end = nullptr;
  char const* s_begin   = s.data();
  char const* s_end     = s.data() + s.size();

  d = std::strtod(s_begin, &parse_end);

  return (parse_end == s_end) && (s.size());
}


The input string view is expected to contain a representation of a double (according to strtod) and no other characters.

In C++17 one could (potentially should) use std::from_chars instead. There are slight differences in the input formats, but from_chars is much faster.
Last edited on
Hello WoodyW00,

Debugging the program is much like writing the program. It should be done in small steps.

This is one suggestion you could work on your program in small steps:
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
76
77
78
#include <cctype>  // <--- Added.
#include <iostream>
#include <iomanip>  // <--- Added.
#include <cstdlib>
#include <ctime>
#include <cmath>
#include <cerrno>   

using namespace std;

const double MAXRANGE = pow(2.0, 16.0); // 65536
const double MINRANGE = -pow(2.0, 16.0);

int main(/*int argc, char* argv[]*/)  // <--- Uncomment after testing.
{
// ******************* Used for testing. Delete When Finished ******************

    constexpr int MAXARGC{ 4 };  // <--- Change for testing.
    constexpr int MAXARGV{ 4 };

    int argc{ MAXARGC };
    char argv[MAXARGV][10]
    {
        "",
        "12.34.56",
        "20.357",
        "-"
    };
// ******************************************************************************
    char* endPtr{ nullptr };
    char operand{};
    bool isARealDouble{};  // <--- Left for now.

    std::cout << std::fixed << std::setprecision(3);  // <--- Added.

    if (argc != 4)
    {
        std::cerr <<
            "\n     Invalid number of arguments\n\n"
            " Should be number1 number2 operand. [12.34 20.56 (+, -, *, /, ^)].\n";
    }

    if (argc == 3)
    {
        operand = '0';  // <--- '0' and 0 are 2 different things and not interchangable.
    }
    else if (argc == 4)
    {
        operand = std::tolower(argv[3][0]);

        if (operand != '+' && operand != '-' && operand != '*' && operand != '/' && operand != '^')
        {
            std::cerr << "\n     Invalid operand!\n     Choices are (+, -, *, / or ^)\n";

            return 1;  // <--- Could replace with a do/while loop to enter a new operand.
        }
    }
    else if (argc < 3)
    {
        cout << "\n     Not enough numbers or operand available!\n";

        //return 2; // <--- Commented out for testing. You may want to use it later.
    }

    if (operand == '+' || operand == '-' || operand == '*' || operand == '/' || operand == '^' || operand == '0')
    {
        std::cout <<
            "\n Entered if statement\n   Operand is \"" << operand << "\"\n\n"
            " Number1 is " << argv[1] << "\n"
            " Number2 is " << argv[2] << "\n";
 
        std::cout 
            << "\n The actual number1 is " << strtod(argv[1], &endPtr) << '\n'
            << " The actual number2 is " << strtod(argv[2], &endPtr) << '\n';
    }

    return 0;  // <--- Not required, but makes a good break point.
}

You could still check if "argc" is greater than 4, but it really does not matter because the program does not use anything past "argv[3]".

For the moment I have left line 65, but if you rewrite line 55 this would not be needed, I think, I have not gone past this point yet. But it does seem to be redundant.

Because of the way the functions "stof", "strtof" and "strtod" work I could use some clarification on what you consider what an invalid floating point number is and how you want to deal with it.

Andy
Topic archived. No new replies allowed.