std::stod ignores nonnumerical values after decimal place - c++

I am reading in a value with a string, then converting it to a double. I expect an input such as 2.d to fail with std::stod, but it returns 2. Is there a way to ensure that there is no character in the input string at all with std::stod?
Example code:
string exampleS = "2.d"
double exampleD = 0;
try {
exampleD = stod(exampleS); // this should fail
} catch (exception &e) {
// failure condition
}
cerr << exampleD << endl;
This code should print 0 but it prints 2. If the character is before the decimal place, stod throws an exception.
Is there a way to make std::stod (and I'm assuming the same behavior also occurs with std::stof) fail on inputs such as these?

You can pass a second argument to std::stod to get the number of characters converted. This can be used to write a wrapper:
double strict_stod(const std::string& s) {
std::size_t pos;
const auto result = std::stod(s, &pos);
if (pos != s.size()) throw std::invalid_argument("trailing characters blah blah");
return result;
}

This code should print 0 but it prints 2.
No, that is not how std::stod is specified. The function will discard whitespace (which you don't have), then parse your 2. substring (which is a valid decimal floating-point expression) and finally stop at the d character.
If you pass a non-nullptr to the second argument pos, the function will give you the number of characters processed, which maybe you can use to fulfill your requirement (it is not clear to me exactly what you need to fail on).

Related

Converting string to double with no manipulation [duplicate]

I am reading in a value with a string, then converting it to a double. I expect an input such as 2.d to fail with std::stod, but it returns 2. Is there a way to ensure that there is no character in the input string at all with std::stod?
Example code:
string exampleS = "2.d"
double exampleD = 0;
try {
exampleD = stod(exampleS); // this should fail
} catch (exception &e) {
// failure condition
}
cerr << exampleD << endl;
This code should print 0 but it prints 2. If the character is before the decimal place, stod throws an exception.
Is there a way to make std::stod (and I'm assuming the same behavior also occurs with std::stof) fail on inputs such as these?
You can pass a second argument to std::stod to get the number of characters converted. This can be used to write a wrapper:
double strict_stod(const std::string& s) {
std::size_t pos;
const auto result = std::stod(s, &pos);
if (pos != s.size()) throw std::invalid_argument("trailing characters blah blah");
return result;
}
This code should print 0 but it prints 2.
No, that is not how std::stod is specified. The function will discard whitespace (which you don't have), then parse your 2. substring (which is a valid decimal floating-point expression) and finally stop at the d character.
If you pass a non-nullptr to the second argument pos, the function will give you the number of characters processed, which maybe you can use to fulfill your requirement (it is not clear to me exactly what you need to fail on).

How do I properly parse substrings so that they can be effective for my newbie calculator?

Please ELI5 if possible, since i've only been coding for a few days and this is my first program! Below is a portion of my script that is supposed to interpret a single line of input that somebody enters (like "5+5" or something).
I have other operations that I want to add later that are formatted differently, which is why I'm using string instead of a switch function or something.
Anyways.. this isn't working :( So below is my logical process and maybe somebody can point out where I messed up? :)
Thank you in advance!
if (fork.find("+" && "-" && "x" && "/"))
{
size_t pos = fork.find("+" && "-" && "x" && "/"); // Defines a position at the operator symbol
string afterfork = fork.substr(pos + 1); // Cuts a substring at the operator symbol + 1
size_t beforepos = fork.find_first_of(fork); // Defines a position at the beginning of the string
string beforefork = fork.substr(beforepos); // cuts a substring at the begninning of the string
string atfork = fork.substr(pos); // cuts a substring that only has one char (the operator +, -, x, etc)
int x = stoi(beforefork.c_str()); // converts the first substring to an integer
int y = stoi(afterfork.c_str()); // converts the third substring to an integer
string operation = atfork; // converts the middle substring that only has one char to a different name.
return input(x, operation, y); // will send this information to the input function (which will do the math for the calculator).
}
To search the string for one of a list of characters, you can use find_first_of. This function returns npos if it didn't find anything.
const size_t operatorPos = input.find_first_of("+-*/");
if (operatorPos == std::string::npos) {
std::cout << "Couldn't find an operator!\n";
return;
}
To split the string into two sub-strings, you can use substr. To get a character at a position, use operator[].
const std::string left = input.substr(0, operatorPos);
const std::string right = input.substr(operatorPos + 1);
const char operation = input[operatorPos];
To convert a string to an integer, well, there are a lot of options. I'll use std::stoi for this answer. This function throws an exception that we need to catch when it can't convert the string to an integer.
int leftInt;
try {
leftInt = std::stoi(left);
} catch (...) {
std::cout << '"' << left << "\" is not a valid integer!\n";
return;
}
int rightInt;
try {
rightInt = std::stoi(right);
} catch (...) {
std::cout << '"' << right << "\" is not a valid integer!\n";
return;
}
If exceptions are really confusing (it took me ages to get my head around exceptions!) then you can try another function. My favourite (and IMO best) is std::from_chars. Another option is to just not catch the exception.
const int leftInt = std::stoi(left);
const int rightInt = std::stoi(right);
In that case, you won't get a nice error message like "five" is not a valid integer!. You'll get something like:
libc++abi.dylib: terminating with uncaught exception of type std::invalid_argument: stoi: no conversion
Abort trap: 6
Try running std::stoi("five") and see for yourself!
Please, don't use using namespace std;. Just don't!

how to stop integer variable from taking double input (digits before decimal) and any other values?

Why doesn't the .fail() work if I put any double or integer with character as input for the last vabiable e?
I have added some output pictures for this problem.
Code:
int main() {
string a,b,line;
double c;
int d,e;
stringstream ss;
getline(cin,line);
ss<<line;
ss>>a>>b>>c>>d>>e;
cout<<"command: "<<a<<endl<<"arg1: "<<b<<endl<<"arg2: "<<c<<endl<<"arg3: "<<d<<endl<<"arg4: "<<e<<endl;
if(ss.fail())
cout<<"Error: invalid command"<<endl;
else
cout<<"perfect"<<endl;
return 0;
}
How can I fix this problem?
>> stops reading as soon as it finds input that cannot be parsed into whatever data type it's been told to read into. Input of 7.5 read into an int is a perfectly acceptable 7 and the .5, which cannot be part of an int, is left in the stream to bedevil the next read from the stream. If OP had input 7.5 for the third argument (int d), the read of .5 into the fourth argument (int e) would have failed.
Ah. Totally neglected the How To Fix part.
My personal preference is to read all data in as a string and parse it myself. In this case I'd use good ol' strtol, mostly because I haven't warmed to the idea of throwing exceptions over bad user input. Typos happen. They happen too often to be exceptional. Deal with it.
So we would read into std::string e, not int e and then...
char * endp; // strtol will point endp to character that ended the parsing.
// If that's not the end of the string there was stuff we couldn't parse
errno = 0; // clear the error code so we can catch an overflow error
// if the number parsed was too big
long num = std::strtol(e.c_str(), &endp, 10); // parse the string as a decimal number
if (*endp != '\0' || errno == ERANGE)
{
// handle error. Abort, nag user and repeat, whatever
}
// use num
OP has added that they are not permitted to use C library calls. So be it. The C++ library equivalent is std::stoi. My rant on exceptions above explains why I don't like this option, but here we go!
size_t end;
int num = std::stoi(e, &end); // parse the string as a decimal number
if (end != e.length())
{
// handle error. Abort, nag user and repeat, whatever
}
// use num
If it utterly fails to convert, std::stoi will throw std::invalid_argument. If the number provided was too big, it throws std::out_of_range, so either catch and handle the exceptions or let the program abort. Your call.
template< typename From,typename To>
static inline bool superConvert(const From& fromVar,To& toVar)
{
stringstream ss;
ss<<fromVar;
ss>>toVar;
if(ss.fail())
{
return false;
}
else
{
From tempFrom;
stringstream ss;
ss<<toVar;
ss>>tempFrom;
if(tempFrom != fromVar)
{
return false;
}
else
{
return true;
}
}
}
For integers this function works fine. Since it double check, the proper way is to use stdol but in case if it is not allowed u can use this.
int i;
bool convertSuccess = superConvert<string,int>("25",i);
cerr<<i;

Parse and convert denorm numbers?

In C++, we can store denorm numbers into variables without problems:
double x = std::numeric_limits<double>::denorm_min();
Then, we can print this variable without problems:
std::cout<<std::setprecision(std::numeric_limits<double>::max_digits10)
std::cout<<std::scientific;
std::cout<<x;
std::cout<<std::endl;
And it will print:
4.94065645841246544e-324
But a problem occurs when one tries to parse this number. Imagine that this number is stored inside a file, and read as a string. The problem is that:
std::string str = "4.94065645841246544e-324";
double x = std::stod(str);
will throw an std::out_of_range exception.
So my question is: how to convert a denorm value stored in a string?
I'm not sure I have understood the problem, but using std::istringstream like this:
std::string str = "4.94065645841246544e-324";
double x;
std::istringstream iss(str);
iss >> x;
std::cout << std::setprecision(std::numeric_limits<double>::max_digits10);
std::cout << std::scientific;
std::cout << x << std::endl;
...gives me:
4.94065645841246544e-324
Apparently, you can use the strtod (or the older atof) interface from cstdlib. I doubt whether this is guaranteed or portable.
I'm not sure if it will make a difference, but you are actually printing:
(std::numeric_limits<double>::max_digits10 + 1) = 18 decimal digits.
e.g., an IEEE-754 64-bit double with round-trip precision is "1.16" in scientific notation. Perhaps this is introducing some ULP / rounding that interferes with the conversion?
The problem with denormals and std::stod is that the latter is defined in terms of std::strtod, which may set errno=ERANGE on underflow (it's implementation-defined whether it'll do, and in glibc it does). As reminded by gcc developers, in such a case std::stod is defined by the standard to throw std::out_of_range.
So your proper workaround is to use std::strtod directly, ignoring ERANGE when the value it returns is finite and nonzero, like here:
double stringToDouble(const char* str, std::size_t* pos=nullptr)
{
errno=0;
char* end;
const auto x=std::strtod(str, &end);
if(errno==ERANGE)
{
// Ignore it for denormals
if(x!=0 && x>-HUGE_VAL && x<HUGE_VAL)
return x;
throw std::out_of_range("strtod: ERANGE");
}
else if(errno)
throw std::invalid_argument("strtod failed");
if(pos)
*pos=end-str;
return x;
}
Note that, unlike std::istringstream approach suggested in another answer, this will work for hexfloats too.

testing for double in visual c++

I am designing a gui in visual c++ and there is a textbox where user inputs values so a calculation can be performed. How do I validate the input to ensure it can be cast to a double value?
In any C++ environment where you have a std::string field and wish to check if it contains a double, you can simply do something like:
#include <sstream>
std::istringstream iss(string_value);
double double_value;
char trailing_junk;
if (iss >> double_value && !(iss >> trailing_junk))
{
// can use the double...
}
As presented, this will reject things like "1.234q" or "-13 what?" but accept surrounding whitespace e.g. " 3.9E2 ". If you want to reject whitespace, try #include <iomanip> then if (iss >> std::noskipws >> double_value && iss.peek() == EOF) ....
You could also do this using old-style C APIs:
double double_value;
if (sscanf(string_value.c_str(), "%lf%*c", &double_value) == 1)
You cannot "cast" a string to a double, you can only convert it. strtod function will return a pointer to the character within the string where the conversion stopped, so you can decide what to do further. So you can use this function for conversion AND checking.
I'd recommend Boost's lexical_cast, which will throw an exception if the conversion fails.
Since this seems to be a C++ CLI related question and your string from the textbox might be a .NET string, you might want to check the static Double::Parse method. For more portable solutions see the other answers...
As stated already, strtod(3) is the answer.
bool is_double(const char* str) {
char *end = 0;
strtod(str, &end);
// Is the end point of the double the end of string?
return end == str + strlen(str);
}
To address #Ian Goldby's concern, if white space at the end of the sting is a concern, then:
bool is_double(const char* str) {
char *end = 0;
strtod(str, &end);
// Is the end point of the double plus white space the end of string?
return end + strspn(end, " \t\n\r") == str + strlen(str);
}
Simply convert it to a double value. If it succeeds, the input is valid.
Really, you shouldn't be writing your own rules for deciding what is valid. You'll never get exactly the same rules as the library function that will do the actual conversion.
My favourite recipe is to use sscanf(), and check the return value to ensure exactly one field was converted. For extra credit, use a %n parameter to check that no non-whitespace characters were left over.