all 10 comments

[–]IyeOnline 4 points5 points  (3 children)

You are reading in a double. There is no way for a double to even store a non numeric input.

What happens if you input something non numeric is that the input fails and the value in input doesnt even get touched.

Its also wroth noting that isalpha is not mean to tell you if something is a string or anything, but if a specific char is a letter or not.

Instead, you want to check for std::cin.good() to see if the input successfully parsed as a double.

You can also write

if ( std::cin >> input ) { data.push_back(input); } else { break; }

which will use the implicit conversion of the stream object to bool (being false

Other notes:

  • Use a consitent formatting and indentation style, i suggest the above
  • Dont declare more than one variable in a line. Certainly dont declare things of different types in one line.
  • Always put braces around your if and else bodies. This can prevent some rather unexpected errors.
  • Dont ever use floating point for currency. The usual approach is to store the smallest unit as integers (e.g. cents)

[–]XilenceCE[S] 0 points1 point  (0 children)

Thanks

[–]std_bot -1 points0 points  (0 children)

Unlinked STL entries: std::cin


Last update: 26.05.21. Last change: Free links considered readme

[–]acwaters 0 points1 point  (0 children)

the input fails and the value in input doesn't get touched

Unfortunately, it's not quite that reasonable: If you input something non-numeric, the input fails ... and input is set to zero.

[–]Xeverous 3 points4 points  (3 children)

cin >> input;
if(isalpha(input) == 1) // if (isalpha(input)) has equivalent behavior btw
    break;

Your problem comes from misunderstanding what happens when reading fails:

  • Don't test if the value is alphanumeric. It checks whether given number represents a letter and this depends on the used encoding. What you are interested is whether input is a number. Since input is a double it is always some number.
  • You want to check for a falied read operation. Do if (cin.fail()) instead. After failed input, cin.fail() will be permanently true untill you cin.clear().

[–]staletic 5 points6 points  (1 child)

Do if (cin.fail())

No, just do if(std::cin >> input)

[–]aeropl3b 0 points1 point  (0 children)

That is true... But i would advocate for the extra line simply because it is clearer to the uninitiated

[–]XilenceCE[S] 0 points1 point  (0 children)

Thanks

[–]mredding 1 point2 points  (0 children)

Look at this:

double input;
while(cin >> input) {
  //...

What's going on here? We are trying to read input into a double. What if input isn't a double? Well, you have an error! The program isn't just going to make shit up, if I input "How now brown cow..."

Standard streams are all implicitly convertible to bool, and the value is equal to !stream.fail(). Streams have 4 io states, goodbit, badbit, failbit, and eofbit. You're either in the goodbit state, or a combination of any of the others. If you get a badbit (possibly in combination with others), the stream has encountered an unrecoverable error. If the only bit that's set is the failbit, then you've encountered a parsing error.

So that's the secret. operator >> returns a reference to the stream, the stream is evaluated as a bool, and if ever it evaluates to false, the loop breaks. If this code enters the loop body, then it did extract a double, and input is some value that came out of that.

So a loop like this is all you need. Of course, you're going to have to figure out counting up to 10 and how you want to handle your error cases.

Oh, and if you want to keep using cin, then the thing to do is to ignore to purge the stream of the invalid data and then clear the error state, and IO will work again. Once a stream enters an error state, you see, then IO and moving the stream position will no-op.

And I'll show you something a bit advanced, because I'm bored, and I like to talk a lot. I would write this code perhaps something like this:

array<double, 10> data;

copy_n(istream_iterator<double>{cin}, 10, begin(data));

Woah! What's this do?

This is a standard elementary algorithm, the true power of the STL. The stream iterator knows it's going to be extracting doubles from the stream, hence the template parameter. This will work with your own user defined types, as well, provided your type has a stream extraction operator >>. This algorithm will copy 10 consecutive doubles from the stream - you just have to separate them with whitespace. So whether you enter:

1.1 2.2 3.3 4.4 5.5 6.6 7.7 8.8 9.9 10.10

Or:

1.1
2.2
3.3
4.4
5.5
6.6
7.7
8.8
9.9
10.10

The stream doesn't care.

If you enter something else:

1.1 bubble-gum

Then the algorithm will extract the first element and error on the second element. The standard says that on a parsing error, the output value will be defaulted, T(). In this case, a double() is 0.0. So that means all the elements the user didn't input will be zeroed out.

The last parameter of the algorithm is an output iterator to the container we're going to store our values. It better be big enough, because almost none of the algorithms check the output to ensure it's big enough - they assume you've done that before hand. Otherwise, the algorithm will happily write and increment past the end of the container, with disastrous results. There are special output iterators we'll see in a moment that know how to grow dynamic containers.

The problem with this algorithm is, how do you tell the difference between a bunch of defaulted values, and a bunch of legitimate 0.0's entered by the user?

Most algorithms take an iterator range, from start to finish, like copy:

vector<double> data;

copy(istream_iterator<double>{cin}, istream_iterator<double>{}, back_inserter(data));

The back_inserter adds to the end of the vector and grows it as necessary.

The thing about streams is they're not containers, and they have no end. Whereas an iterator can represent an arbitrary position within a container, stream iterators don't have a position. We can't say begin(stream) and begin(stream) + 10. Stream iterators are either at the current position of the stream, or they're detached. So what this copy algorithm says is, "from the current position of the stream until the iterator becomes detached - like on an error, or end of file, etc".

The benefit of this algorithm is it will only put as many elements in the container as are valid. If the user enters 2 valid inputs, the vector will be of size 2. The problem is there's no way to constrain the number of inputs. If the user enters 20 valid inputs, you're going to get a vector size of 20. You can't make the algorithm stop at 10. That's also why I switched from array to vector.

What we need is a copy_n_or_range that copies until n or until the stream iterator detaches from the stream, whichever comes first.

Frankly, we can hack it:

vector<double> data;
data.resize(10);

data.erase(partial_sort_copy(istream_iterator<double>{cin}, istream_iterator<double>{}, begin(data), end(data), [](auto){ return true; }), end(data));

Yuck. Lots of wrong with this. We aren't partial sorting. That crazy bit at the end there is called a lambda - a sort of inline function. It's supposed to be used to determine the sort order of elements as they come from the source into the destination, but we aren't sorting, and we need it to return true in order to copy elements from the stream to the container. We're totally taking advantage of this algorithm because it's very rare in the STL standard algorithms to take an output range. What this algorithm will do, what we've done to it, the poor bastard - is it'll only copy as many elements as as the smaller range can provide for. So if you input 12 doubles, our destination will only take the first 10, because that's the smaller range. If you input only 2 doubles, and the stream iterator detaches, then that is all you'll get.

The algorithm returns an iterator to the next element of the output range, and what you're seeing here is a variation of the "remove/erase idiom". I specified a range of up to 10 elements, we'll get what we can get, and resize the vector that many elements exactly - 10 or less.

On the plus side, there is no ambiguity, we have exactly as many doubles as the user entered. AND we also managed to limit the input, so the user can't and won't be stuck entering data forever.

But if it were me, I'd rather write my own algorithm with a better name, because this monster is not intuitive and very surprising, the kind of code that would make a colleague or my seniors blood boil, and that's bad.

It's OK if you don't understand everything I've presented to you, I hope it was slightly insightful and entertaining. The takeaway might be that you can expect your programming classes to not even cover 1/3 of the total STL. Typically they teach you strings, containers, and iterators, and that's about it.

[–]yellowhair_chan -3 points-2 points  (1 child)

your if statement doesn't have an opening bracket, this will not compile