Std::Stod Throws Out_Of_Range Error for a String That Should Be Valid

std::stod throws out_of_range error for a string that should be valid

Converting “7.63918e-313”

The C++ standard allows conversions of strings to double to report underflow if the result is in the subnormal range even though it is representable.

7.63918•10-313 is within the range of double, but it is in the subnormal range. The C++ standard says stod calls strtod and then defers to the C standard to define strtod. The C standard indicates that strtod may underflow, about which it says “The result underflows if the magnitude of the mathematical result is so small that the mathematical result cannot be represented, without extraordinary roundoff error, in an object of the specified type.” That is awkward phrasing, but it refers to the rounding errors that occur when subnormal values are encountered. (Subnormal values are subject to larger relative errors than normal values, so their rounding errors might be said to be extraordinary.)

Thus, a C++ implementation is allowed by the C++ standard to underflow for subnormal values even though they are representable.

Converting std::numeric_limits::min()

Regarding your observation that std::numeric_limits<double>::min() “cannot be parsed” either (I presume you mean it also reports underflow), this may be due to the fact that you converted std::numeric_limits<double>::min() to a string containing a decimal numeral, and that decimal numeral was not an exact representation of std::numeric_limits<double>::min(). If it was rounded down, it is slightly less than min(), and hence it is also in the subnormal range. Thus, attempting to convert that decimal numeral back to a double may correctly report it is below the normal range.

std::numeric_limits::min() is not the minimum double

Regarding your observation that std::numeric_limits<double>::min() is not the minimum double, that is correct. std::numeric_limits<double>::min() is specified by the C++ standard to be the minimum positive normal value. There may be subnormal values below it.

Normal and subnormal values

For IEEE-754 64-bit binary floating-point, the normal range is from 2-1022 to 21024-2971. Within this range, every number is represented with a signficand (the fraction portion of the floating-point representation) that has a leading 1 bit followed by 52 additional bits, and so the error that occurs when rounding any real number in this range to the nearest representable value is at most 2-53 times the position value of the leading bit.

In additional to this normal range, there is a subnormal range from 2-1074 to 2-1022-2-1074. In this interval, the exponent part of the floating-point format has reached its smallest value and cannot be decreased any more. To represent smaller and smaller numbers in this interval, the significand is reduced below the normal minimum of 1. It starts with a 0 and is followed by 52 additional bits. In this interval, the error that occurs when rounding a real number to the nearest representable value may be larger than 2-53 times the position value of the leading bit. Since the exponent cannot be decreased any further, numbers in this interval have increasing numbers of leading 0 bits as they get smaller and smaller. Thus the relative errors involved with using these numbers grows.

For whatever reasons, the C++ has said that implementations may report underflow in this interval. (The IEEE-754 standard defines underflow in complicated ways and also allows implementations some choices.)

Explicit specification of the double precision representation used by compiler

Unfortunately, std::stod is badly designed, because it is not possible to determine what caused the std::out_of_range exception.

I'd suggest you to use strtod instead. While is not specified in the standard what this function should do for subnormal numbers, it behaves well for subnormal numbers usually (it means that it returns subnormal numbers). The benefit of this function is that it returns a meaningful result for out-of-range situations, so it is possible to determine the cause of out-of-range.

If you want to handle out of range situations, you'll need to check errno for ERANGE. Note, that if a subnormal/zero number is a result, then maybe errno will be set to ERANGE, which you should ignore (you can check this out with fpclassify).

So the logic is something like this:

double r = strtod(string, &end);
// here, check for end to know about invalid strings

if (errno==ERANGE) { // out-of-range (overflow, underflow)
int c = fpclassify(r);
if (c!=FP_SUBNORMAL&&c!=FP_ZERO) { // let's filter out underflow cases
// "real" out of range handling here, just overflow
}
}

terminate called after throwing an instance of 'std::out_of_range'

It looks to me as if this is due to a typo, and you should use the variable 'j' in the second loop.
After the first loop,

for (i = 0; i < 52; i++)
{
nums.push_back(i);
}

the variable 'i' contains the value 52, so it sounds expected that calling nums.at(i) would throw a std::out_of_range, since nums only contains 52 values, starting at index 0.

for(int j = 0; j < 52; j++)
{
cout << nums.at(i) << "\n";
}

Fix it by replacing the argument of at() with 'j', which I assume was the original intent:

for(int j = 0; j < 52; j++)
{
cout << nums.at(j) << "\n";
}

C++ std::stod. How to make user enter a number?

The stod function throws an exception when it does not parse correctly. So you could write:

double d;

for (;;)
{
std::string s;
std::getline(std::cin, s);
if ( !std::cin )
{ d = 0; break; } // or some other handling that exits the loop

try
{
d = std::stod(s);
}
catch(...)
{
std::cout << "Invalid input, try again.\n");
}
}

Is std::stoi actually safe to use?

Does std::stoi throw an error on the input "abcxyz"?

Yes.

I think your confusion may come from the fact that strtol never reports an error except on overflow. It can report that no conversion was performed, but this is never referred to as an error condition in the C standard.

strtol is defined similarly by all three C standards, and I will spare you the boring details, but it basically defines a "subject sequence" that is a substring of the input string corresponding to the actual number. The following four conditions are equivalent:

  • the subject sequence has the expected form (in plain English: it is a number)
  • the subject sequence is non-empty
  • a conversion has occurred
  • *endptr != nptr (this only makes sense when endptr is non-null)

When there is an overflow, the conversion is still said to have occurred.

Now, it is quite clear that because "abcxyz" does not contain a number, the subject sequence of the string "abcxyz" must be empty, so that no conversion can be performed. The following C90/C99/C11 program will confirm it experimentally:

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

int main() {
char *nptr = "abcxyz", *endptr[1];
strtol(nptr, endptr, 0);
if (*endptr == nptr)
printf("No conversion could be performed.\n");
return 0;
}

This implies that any conformant implementation of std::stoi must throw invalid_argument when given the input "abcxyz" without an optional base argument.


Does this mean that std::stoi has satisfactory error checking?

No. The person you were talking to is correct when she says that std::stoi is more lenient than performing the full check errno == 0 && end != start && *end=='\0' after std::strtol, because std::stoi silently strips away all characters starting from the first non-numeric character in the string.

In fact off the top of my head the only language whose native conversion behaves somewhat like std::stoi is Javascript, and even then you have to force base 10 with parseInt(n, 10) to avoid the special case of hexadecimal numbers:

input      |  std::atoi       std::stoi      Javascript      full check 
===========+=============================================================
hello | 0 error error(NaN) error
0xygen | 0 0 error(NaN) error
0x42 | 0 0 66 error
42x0 | 42 42 42 error
42 | 42 42 42 42
-----------+-------------------------------------------------------------
languages | Perl, Ruby, Javascript Javascript C#, Java,
| PHP, C... (base 10) Python...

Note: there are also differences among languages in the handling of whitespace and redundant + signs.


Ok, so I want full error checking, what should I use?

I'm not aware of any built-in function that does this, but boost::lexical_cast<int> will do what you want. It is particularly strict since it even rejects surrounding whitespace, unlike Python's int() function. Note that invalid characters and overflows result in the same exception, boost::bad_lexical_cast.

#include <boost/lexical_cast.hpp>

int main() {
std::string s = "42";
try {
int n = boost::lexical_cast<int>(s);
std::cout << "n = " << n << std::endl;
} catch (boost::bad_lexical_cast) {
std::cout << "conversion failed" << std::endl;
}
}

How can I fix it? libc++abi.dylib: terminating with uncaught exception of type std::invalid_argument: stoi: no conversion

From cppreference:

Exceptions: std::invalid_argument if no conversion could be performed

Looks like one of the strings that you're operating on cannot be converted to an integer, and therefore stoi is throwing an exception. In general, it's a good idea to know about all the failure modes of functions you're invoking -- you'll want to handle this case by catching this exception somewhere in your program logic (or by guaranteeing that the strings sent to stoi will always be convertible, which may be possible in some use cases).



Related Topics



Leave a reply



Submit