basic invalid number detection (command arguments)

The question is..

if you use the atof() function, which is used to convert a string to a floating‐point number (type double), the only valid argument to pass into the atof() function in the command line arguments phase ‘Hello World 123.54’ is ‘123.54’, as this could be the number one hundred and twenty three point fifty‐four. The other arguments ‘Hello’ and ‘World
’ would result in 0, as the characters in each of those arguments does not represent a valid numerical number.

Your  task is  to extend your program  to determine if  the characters in each argument are suitable  for 
conversion  to  a  double  (which  is  a  larger  size  float).  Your  program  should  handle  a  minimum  of  3 
arguments  and  display  the  text  “invalid  input”  if  the  argument  contains  characters  other  than  an 
alphanumerical characters and one '.' decimal point. If  the argument contains valid alphanumerical 
characters and one decimal point, then it should be converted to a double and displayed to the console 
screen. Each input argument should be processed individually and the output should be on a separate 
line in the console. For example if the command line arguments phrase ‘Hello World 123.54’ was used, 
the console output would be: 
invalid input
invalid input
123.54


I tried very hard but I can't really figure out anything.

Could anyone please suggest codes for this problem? I tried using atof(), nested for loop, if, string. but I can't seem to make it working.

#include "stdafx.h"
#include <iostream>
#include <string>
#include <cctype>
#include <cstdlib>

using namespace std;

bool validDouble(const char *arg)
{

double outcome = atof(argv[i])
// your code here
}



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

{
for (int i = 1; i < argc; i++)
{
if (validDouble(argv[i]))
{
double outcome = atof(argv[i]);
cout << outcome << endl;
// convert argv[i] to double and display it
}
else
{
cout << "invalid input" << endl;
// output error msg
}
}



return 0;
}
Last edited on
Here is a program that will solve the problem. I have left out the code that actually determines whether the argument appears to be a double (the body of function isDouble()).

Hope this helps,
Dave

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <iostream>

// examine the argument and return true if it's a valid number, false
// otherwise
bool isDouble(const char *arg)
{
    // INSERT YOUR CODE HERE
}


int
main(int argc, char **argv)
{
    // Loop through the arguments. Note that argv[0] is the name of the program
    // so the "real" arguments start at argv[1]
    for (int i=1; i<argc; ++i) {
	if (isDouble(argv[i])) {
	    double d = atof(argv[i]);
	    std::cout << d << '\n';
	} else {
	    std::cout << "invalid input\n";
	}
    }
}
It would be better if you divide this task in 2 parts.
One parts checks if one argument is correct and the second part loops through all the arguments.
Like so:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
bool validDouble(const char *arg)
{
  // your code here
}


int main(int argc, char *argv[])
{
  for (int i = 1; i < argc; i++)
  {
    if (validDouble(argv[i]))
    {
      // convert argv[i] to double and display it
    }
    else
    {
      // output error msg
    }
  }
  system("pause");
  return 0;
}


BTW. I recommend to do a google search for "divide and conquer" Dividing a problem into smaller sub-problems makes things much easier.
what do I need to do in the body of bool function?

atof(argv[i]) != 0 ??
it will return '0' if the input is not double.
what do you get if the user is a jerk and types hello 0 world 0.0 ? :)

I think youll want to check to see if the argument actually IS zero.

You *may* want to handle alternative local format of , instead of . (??) some countries reverse the meaning of , and . so 123.45 is 123,45 and 1,234 is 1.234. The default is the american way.

Last edited on
As an alternative to atof(), you could use strtod()

See example in reference page:
http://www.cplusplus.com/reference/cstdlib/strtod/

If the input string contains a number and nothing else, after the function call the pointer pEnd should point to the terminating null character. You might use that to test for valid result.

closed account (48T7M4Gy)
Maybe this - perhaps make a function to detect and report on characters.

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
#include <iostream>

using namespace std;

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

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

{
    // Hardcode arguments for testing only
    argc = 4;
    argv[0] = "Hello";
    argv[1] = "world";
    argv[2] = "0";
    argv[3] = "123.45";
    
    bool isARealDouble = true;
    char character = ' ';
    
    // check out each argument
    for (int i = 0; i < argc; i++)
    {
        isARealDouble = true;
        
        // check out the arguments individual characters
        for(int j = 0; j < strlen(argv[i]); j++)
        {
            character = argv[i][j];
            cout << character << ' ';
            
            if(character == '.' or isdigit(character) )
                isARealDouble = true;
            else
                isARealDouble = false;
            
        }
        
        if(isARealDouble)
            cout << " is a double" << endl;
        else
            cout << " is not a double" << endl;
    }
    
    return 0;
}


H e l l o  is not a double
w o r l d  is not a double
0  is a double
1 2 3 . 4 5  is a double
Program ended with exit code: 0

closed account (48T7M4Gy)
There is a (small) bug in the if(character ... etc logic which you'll need to fix because if the last character is true then all previous checks which might be false are overridden :)
closed account (48T7M4Gy)
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
#include <iostream>
#include <cstring>

using namespace std;

bool validDouble(const char *argument)
{
    int count_points = 0;
    for(int j = 0; j < strlen(argument); j++)
    {
        if(argument[j] == '.')
            count_points++;
        
        if(count_points > 1 or (argument[j] != '.' and !isdigit(argument[j])) )
            return false;
    }
    return true;
}

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

{
    // Hardcode arguments for testing only
    argc = 4;
    argv[0] = "Hel.lo";
    argv[1] = "world";
    argv[2] = "0";
    argv[3] = ("1.23.45");
    
    // check out each argument
    for (int i = 0; i < argc; i++)
    {
        cout << argv[i] << " is";
        validDouble(argv[i])? cout << " (" << atof(argv[i]) << ")": cout << " not";
        cout << " a double\n";
    }
    
    return 0;
}


Late news: Given OP problem statement stod() is the appropriate function in line 34, not atof()
Last edited on
> determine if the characters in each argument are suitable for conversion to a double

This is hard, unless we use the standard library; there are too many edge cases, and to make matters worse, the specific characters that can represent a floating point value is locale-dependent.

Chervil's suggestion (use strtod() with error checks) is a sound one.
http://www.cplusplus.com/forum/beginner/219637/#msg1011576

Two other options are to use std::stod() or use an input stream (std::istringstream) to parse the characters.

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
#include <iostream>
#include <string>
#include <iomanip>

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

    for( int i = 1 ; i < argc ; ++i )
    {
        const std::string arg = argv[i] ;
        std::cout << "arv[" << i << "] " << std::quoted(arg) << " : " ;

        std::size_t pos = 0 ;
        double value = 0 ;
        try
        {
            value = std::stod( arg, std::addressof(pos) ) ; // try to interpret arg as a floting point value 
            
            if( pos == arg.size() ) std::cout << value << '\n' ; // fine: fully parsed to the end of the string
            
            else std::cout << "not fully parsed: invalid characters " 
                           << std::quoted( arg.substr(pos) ) << " at the end\n" ;
        }
        catch( const std::out_of_range& )
        {
            std::cout << "range error: value is outside the range of double\n" ;
        }
        catch( const std::invalid_argument& ) // nothing was parsed
        {
            std::cout << "invalid input\n" ;
        }
    }
}

echo '--------------------------- g++ ------------------------------------'
g++ -std=c++14 -pedantic-errors -Wall -Wextra main.cpp && ./a.out hello -123.7 0 123.7.9 1.23e+3 1.23e-5000 INF -0xa.bcdP5
echo '---------------------------- clang++ ---------------------------------'
clang++ -std=c++14 -stdlib=libc++ -pedantic-errors -Wall -Wextra main.cpp && ./a.out hello -123.7 0 123.7.9 1.23e-3 1.23e+5000 NaN 0xa.bcdP-5
--------------------------- g++ ------------------------------------
arv[1] "hello" : invalid input
arv[2] "-123.7" : -123.7
arv[3] "0" : 0
arv[4] "123.7.9" : not fully parsed: invalid characters ".9" at the end
arv[5] "1.23e+3" : 1230
arv[6] "1.23e-5000" : range error: value is outside the range of double
arv[7] "INF" : inf
arv[8] "-0xa.bcdP5" : -343.602
---------------------------- clang++ ---------------------------------
arv[1] "hello" : invalid input
arv[2] "-123.7" : -123.7
arv[3] "0" : 0
arv[4] "123.7.9" : not fully parsed: invalid characters ".9" at the end
arv[5] "1.23e-3" : 0.00123
arv[6] "1.23e+5000" : range error: value is outside the range of double
arv[7] "NaN" : nan
arv[8] "0xa.bcdP-5" : 0.335548

http://coliru.stacked-crooked.com/a/36349317eb7ad4e5
Topic archived. No new replies allowed.