1

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.

Marco
  • 35
  • 1
  • 9
  • 2
    Did you happen to use `cin >> std::hex` earlier in the program – M.M May 26 '16 at 11:13
  • 1
    In stroustrup's example he is probably reading into `int`. The rules for reading a `double` are different and do admit various letters, e.g. `x`, `p`, `e` – M.M May 26 '16 at 11:14
  • 1
    I just tried this and it works for me for "2a" and "2b". Can you give your specific stdin input for which it fails? – Smeeheey May 26 '16 at 11:20
  • Please provide a [MCVE]. [Cannot reproduce](http://ideone.com/1ACqxU). – πάντα ῥεῖ May 26 '16 at 11:22
  • I [can't reproduce](http://ideone.com/bzamAb) with exactly those examples. There must be something more in your code that messes things up (or you didn't reproduce your code exactly). – molbdnilo May 26 '16 at 11:34
  • Hi M.M, No I didn't use std::hex. Yes, you're right - it does work for integers. Could you tell me why it doesn't for doubles? Thanks! – Marco May 26 '16 at 12:27
  • @Marco I have spelled out *exactally* how the `istream` extraction operators work and provided sources on that. Your `double` extraction operator is not compliant, or there's something in your code that's causing the issue. – Jonathan Mee May 26 '16 at 12:35
  • @M.M As it turns out `hex` is for integers and has no effect on a double: http://stackoverflow.com/q/37461993/2642059 – Jonathan Mee May 26 '16 at 14:59
  • @Marco You've accepted my answer, and I appreciate that, but it was just pointing out that the results that you were presenting weren't possible without 1) a non-conformant library 2) there was a state introduced outside the code in your question that caused this. Were you able to confirm what the issue was? Is `cin` already in a bad state when you try to extract the double perhaps? – Jonathan Mee May 26 '16 at 17:45
  • @Jonathan: The problem does seem to lie with the compiler, because it works fine with g++ 4.8.2 on linux, but fails using Apple LLVM version 7.0.0 (clang-700.0.72), which is what comes with the latest version of xcode. I've sent an email to the llvm mailing list to see if they can shed some more light on the matter. – Marco May 26 '16 at 19:08
  • @πάνταῥεῖ Turns out Marco *did* provide an MCVE. This is a bug in LLVM: http://stackoverflow.com/a/37460824/2642059 – Jonathan Mee May 26 '16 at 19:14
  • @molbdnilo Turns out Marco *did* give us complete code. The failure is with LLVM: http://stackoverflow.com/a/37460824/2642059 – Jonathan Mee May 26 '16 at 19:15
  • @Marco Yeah I'v put a link to the bug against it in [my answer](http://stackoverflow.com/a/37460824/2642059) If you happen to rustle up any proposed fix or work around on the mailing list you might consider posting it here as the correct answer. – Jonathan Mee May 26 '16 at 19:24
  • @Jonathan Well what do you know :o)! Thanks very much for your help. If I find out any more I'll be sure to post it. – Marco May 26 '16 at 22:03

2 Answers2

1

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

Jonathan Mee
  • 37,899
  • 23
  • 129
  • 288
  • Wow, thanks for your reply! It'll take me some time to digest, but it looks very helpful! I'll try a few different environments and see if there's any difference. – Marco May 26 '16 at 17:41
  • @Marco Ah, great. I don't know how simple your code is but perhaps you could paste it into http://ideone.com to let me have a look at the unedited version? – Jonathan Mee May 26 '16 at 17:46
  • OK, I've now tried it on two linux machines, both with g++ 4.8.4, and it works fine. The machine where it fails is a Mac, using llvm 7.0.0. You'd hope that there wouldn't be any difference...(?). Do you use a general strategy for avoiding this sort of thing? – Marco May 26 '16 at 18:06
  • ah, hadn't seen your last comment yet - the 'edited' version is the actual one I ran :o). – Marco May 26 '16 at 18:07
  • @Marco Have a +1 you did find a non conformant point on LLVM. I have to say, work arounds are not pretty, they'd involve `sscanf`. Do you control the input? If so I'd say just make sure to space delineate your input. – Jonathan Mee May 26 '16 at 19:13
  • OT, but this issue highlights a big problem area of iostreams. The rules for streaming in a `double` are complicated, change all the time, self-contradictory in places, and all implementations have bugs of some form. – M.M May 26 '16 at 21:40
0

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.

Community
  • 1
  • 1
Marco
  • 35
  • 1
  • 9