Marco
Marco

Reputation: 45

C++ - Reading a double, followed by a character, from cin

I'm trying to read a double, followed by a character, from cin using the snippet:

double d;
char c;
while(1) {
    cin >> d >> c;
    cout << d << c << endl;
}

The peculiar thing is that it works for some characters, but not for others. For example, it works for "2g", "2h", but fails for "2a", "2b", "2x" ...:

mwmbp:ppcpp mwisse$ ./a.out

2a
0
2b
0
2c
0
2g
2g
2h
2h
2i
0h
2x
0h
2z
2z

As pointed out by one of you, it does indeed work for integers. Do you know why it doesn't work for doubles? I have as yet been unable to find information on how cin interprets its input.

Upvotes: 1

Views: 1902

Answers (2)

Jonathan Mee
Jonathan Mee

Reputation: 38949

This is currently a bug on LLVM: https://llvm.org/bugs/show_bug.cgi?id=17782 Way back in 2014 it was assigned from Howard Hinnant to Marshall Clow since then... Well don't hold you breath on this getting fixed any time soon.

EDIT:

The istream extraction operator internally uses num_get::do_get Which sequentially performs these tasks for a double:

  1. Selects a conversion specifier, for double that's %lg
  2. Tests for an empty input stream
  3. Checks if the next character in the string is contained in the ctype or numpunct facets
  4. If scanf would allow the character obtained from 3 to be appended to the input field given the conversion specifier obtained in 1, if so 3 is repeated if not 5 is performed on the input field without this character
  5. The double from the accepted input field is read in with
  6. If 5 fails failbit is assigned to the istream's iostate, but if 5 succeeded, the result is assigned to the double
  7. If any thousands separators were allowed into the input field by facet numpunct in 3 their position is evaluated, if any of them violate the grouping rules of the facet, failbit is assigned to the istream's iostate
  8. If the input field used in 5 was empty eofbit is assigned to the istream's iostate

That's a lot to say that for a double you're really concerned with scanf's %lg conversion specifier's rules for extraction of a double (which internally will depend upon strtof's constraints):

  1. An optional plus or minus character
  2. One of the following
    • "INF" or "INFINITY" (case insensitive)
    • "NAN" (case insensitive)
    • "0x" or "0X", an input field of hexadecimal digits and optionally a decimal point character, and optionally followed by a "p" or "P" a plus or minus sign and a decimal exponent
    • An input field of decimal digits and optionally a decimal point character and optionally an "e" or "E" a plus or minus sign and a non-empty exponent

Note that if your locale defines any other expression as an acceptable floating point input field this is also accepted. So if you've added some special sauce to the istream you're working with that may be where the problem lies. Outside of that, neither a trailing "a", "b", or "x" are an accepted suffix for the %lg conversion specifier, so your implementation is not compliant or there's something else you're not telling us.

Here is a live example of your inputs succeeding on gcc5.1 which is compliant: http://ideone.com/nGGW0L

Upvotes: 1

Marco
Marco

Reputation: 45

Since the problem is caused by a bug (or feature, depending on your point of view), in libc++, it seems that the easiest way to avoid it is to use libstdc++ instead, until a fix is in place. If you're running on a mac, add -stdlib=libstdc++ to your compile flags. g++ -stdlib=libstdc++ test.cpp will correctly compile the code given in this post.

Libc++ appears to have other, similar, bugs, one of which I posted here: Trying to read lines from an ASCII file using C++ , Ubuntu vs Mac...?, before learning about these different libraries.

Upvotes: 0

Related Questions