tags:

views:

69

answers:

5

In java, if I wanted to create some application which could receive both doubles and strings as appropriate input, I would probably do the following:

String input = getInput();//
try { 
    double foo = Double.valueOf(input);
    //Do stuff with foo here 
} catch (NumberFormatException e) { 
    //Do other validation with input
} 

How would you go about doing that in c++? atof() returns 0.0 for invalid input, but how would you differentiate that from a valid double of "0.0"? As an aside, I can only include <iostream>, <string>, <cstdlib>, and <cassert> in this project. I'm assuming I need to use cin in some way, but how can you grab the original input after cin fails to parse some string as a double?

Edit: I could probably use the following, but as I said before, I'm not allowed to import <sstream> on this assignment for some reason

string input;
getline(cin, input);

double x;
istringstream foo(input);
foo >> x
if(cin){
    //do manipulations with x
}
else{
    //since it's not a number, check if input is a valid command etc..
}
A: 

The C++ standard library seems to avoid exceptions for a lot of things that people might expect them, and this may not be the answer that you want to hear but parsing doubles isn't exactly rocket science so maybe it'd be ok to do it "by hand"?

One note if you do this, you'll probably want to collect it into an integer and then convert to a double and divide by 10^number_of_digits_after_decimal_point.

dash-tom-bang
+2  A: 

C++ streams associate the good, bad and fail state flags. These are stored in ios_base::goodbit, ios_base::badbit and ios_base::failbit respectively, but are commonly accessed through ios::good(), ios::bad() and ios::fail(). There's also ios_base::eofbit and ios::eof() but let's ignore that for the moment.

If parsing fails then the bad bit raises. Also, stream objects are convertible to a boolean-compatible type. If a stream is in a good state, then the statement

if( stream )

evaluates stream as true. Otherwise, it evaluates it as false. With this at hand, grabbing a value from standard input boils down to this:

#include <iostream>

// ...
double d = 0;
if ( std::cin >> d ) {
    // input is a double. handle that here.
} else {
    // input isn't a double. handle that here.
}
wilhelmtell
I understand the basic functionality of cin, but what I'm trying to avoid is prompting the user a second time for specific input, as both doubles and characters are valid input for the program. Is there a way to retrieve the original input after it fails to parse the Double. I may be way off base, but something like cin.seekg(-1 * cin.gcount(), ios::cur); to try again?
Grantismo
how to parse the input, if it isnt a double ? normally i would just read the input into a `std::string`, then reinsert it into a `std::stringstream`, and then check if its a double or not...
smerlin
that's what I was planning on doing, but our professor is limiting us to the libraries I mentioned in my original post, unfortunately sstream and fstream are not an option.
Grantismo
+3  A: 

Exceptions should be reserved for exceptional situations. While you certainly can abuse them like this, it's a lousy idea -- clearly you're pretty much expecting things other than doubles, so treating it as an exception doesn't make any real sense.

The only real question is the exact circumstance under which you want the input treated as a string. Just for example, if the input string was something like "1.23e2 non-numeric data", do you want to just use the "1.23e2" as a number, or do you want to treat the whole thing as a string?

Either way, you'd want to us strtod for the conversion -- the difference is only how you react to what it returns. In particular, strtod takes two parameters instead of just one like atof does. The second parameter is a pointer to pointer to char. Assuming you pass a non-NULL pointer, strtod will set it to point at the last character in the input string that it successfully converted. If it's pointing to the beginning of the input string, nothing was converted. If it's pointing to the end, everything was converted. If it's somewhere in between, part of the string converted, and part didn't.

For the moment, I'm going to assume that you want a double value holding whatever number could be converted at the beginning of the string, and whatever couldn't be converted treated as a string:

#include <stdlib.h>
#include <stdio.h>

int main() { 
    char input[] = "12.34 non-numeric data";
    char *string;
    double value = strtod(input, &string);

    printf("Number: %f\tstring: %s\n", value, string);
    return 0;
}
Jerry Coffin
I was just playing with `strtod` to confirm this before I posted. The only caveat is that I think `endptr` will advance even if just whitespace was skipped. Can anyone confirm this?
torak
@torak: `endptr` will point to whatever's *after* what got converted. If nothing got converted, it will point to the beginning of the input string, even though white space will be skipped across to get to numeric data. So, " 1.2 x" will convert to 1.2, and `endptr` will point to " x", whereas: " x" won't convert, and `endptr` will point to " x".
Jerry Coffin
Thanks for reading my entire post an providing a good answer. I'm probably going to end up using this unless I can hack out something similar with cin. The actual program is a simple reverse polish notation based calculator which reads an input one at a time. Inputs are either decimal numbers to be placed on the stack, or one of a couple reserved characters for special operations, like "q"which quits the program.
Grantismo
+1  A: 

try "strtod" in stdlib.h

aeh
A: 

You could have a look at boost::lexical_cast, which would allow you to write pretty much the equivalent of the Java code:

string input;
getline(cin, input);

try {
    double x = boost::lexical_cast<double>(input);
    //Do manipulations with x
} catch (boost::bad_lexical_cast &) {
    //Do other validation with input
}
spong