0

I am currently trying to write a program at school involving a random number generator for rolling a dice. I have written the code successfully, but I am trying to improve it so that the size of the dice and the number the user is trying to roll can be chosen by the user.

I have written code that does this, and I have also added code that repeats the request for input if the wrong value (ie not one of the dice sizes offered or trying to roll a number outside the range of the dice) or input type (ie var instead of int) is entered. However, if a floating point number is entered, and the number to the left of the floating point is in the correct range of values, it is using that number.

For example:

    int size = 0;
    cout << "Please choose the size of the dice to roll (6, 12 or 20): ";
    cin >> size;

    while (size != 6 && size != 12 && size != 20 || !cin)
    {
        cin.clear();
        cin.ignore(numeric_limits<streamsize>::max(), '\n');
        cout << "Invalid number entered. Choose the size of the dice to roll (6, 12 or 20): ";
        cin >> size;
    }

This will correctly ask to repeat the input if any letters or any numbers that aren't 6, 12 or 20 are entered, but if 20.125 (or any floating point number that is 6.- 12.- or 20.-) is entered it will take the number and move on to the next bit of code. This also happens if I enter a valid number followed by letters (ie 6f).

I tried modifying the condition of the while loop to:

    while (size != 6 && size != 12 && size != 20 || !(cin >> size))
    {
        cin.clear();
        cin.ignore(numeric_limits<streamsize>::max(), '\n');
        cout << "Invalid number entered. Choose the size of the dice to roll (6, 12 or 20): ";
        cin >> size;
    }

And that fixes the problem, and it asks me for another input if I enter 12.5 or 20R etc, but then when I enter a correct input (6, 12 or 20) it just takes me to a new line in the debugger without moving to the next line of code. If I then enter a correct input again, it reads it at takes me to the next line of code.

I don't quite understand why one works 99% how I want it to with one error, and the other fixes that error but then gives me another one.

Thanks in advance, any advice guidance is much appreciated!

AlexDLC
  • 1
  • 1

4 Answers4

0

You could read a float, assign it to an integer variable and then compare the two. Something along these lines:

int integerSize;
float floatSize;
cin >> floatSize;
integerSize = floatSize; //Your program will perform a cast from float to integer, it will essentially truncate whatever appears after the floating point.
if (integerSize != floatSize) //Your program will now perform a cast from integer to float in order to compare the two, but if floatSize was not a whole number, this comparison should return false.
{
  //code to ask for another value
}

That being said, floats (and doubles and long doubles) do experience rounding errors, so as another user suggested, the safest bet is to read a string and parse it yourself, or using a built-in function like std::stoi() (only for integers) and std::stof() (for floats).

Maurycyt
  • 116
  • 7
0

The way cin >> some_int_variable will interpret the input is character by character, until it stops making sense as an int. For instance, when it encounters . or f, cin is done reading that int.

If you want a different behavior, you will have to implement it yourself. Specifically, how do you stop processing one input, and starts processing the next?

cin >> some_int_variable will stop when it is no longer a valid it, cin >> some_std_string_variable will stop when it encounters an white-space character (new lines included). How about your problem? How do you want to separate one input from the next?

If white-space is a sensible approach, you can do so:

std::string word;
std::cin >> word;

int value;
bool error = false;
try {
    size_t pos;
    value = std::stoi(word, &pos);
    // Was the full string used to build the int?
    if(pos != word.size()) error = true;
} catch(std::logic_error&) {
    error = true;
}

if(error) { /* something went wrong */ }

See the docs for std::stoi().

lvella
  • 10,929
  • 10
  • 42
  • 91
  • Hi Ivella, thanks for the advice, what you're saying makes sense (even if the code doesn't, I'm fairly new to this so I would prefer to leave out the feature rather than submit code that I don't understand.) One question though, when I have `!(cin >> diceSize)` in the while condition instead of just `!cin`, it does what I want it to in that it will not read 12.4 or 6f etc as an int and will correctly tell me to input a number again, but then if I input a correct value (6 12 or 20) it doesn't seem to register them. – AlexDLC Oct 07 '20 at 15:02
  • Because `cin >> diceSize` tries to read the input again. If the next character is a number, it works and overwrites the previous value of `diceSize`, so you lose it. If the next character is `.` or `R`, it fails, and you get the error message. Also, see this: https://stackoverflow.com/questions/5605125/why-is-iostreameof-inside-a-loop-condition-i-e-while-stream-eof-cons – lvella Oct 07 '20 at 15:15
0
template<typename T>
T typed_read_line(const std::string& prompt)
{
   T result{};
   std::string l;
   std::cout << prompt << ":";
   while (std::getline(std::cin, l)) {
      std::istringstream line{l};
      line >> result >> std::ws;
      if (line.eof()) break;
      //line contains more then expected
      std::cout << "Again: " << prompt << ":"; 
   }
   return result;
}

https://godbolt.org/z/dfYKe3

Or version with extra validation or v3.

Marek R
  • 23,155
  • 5
  • 37
  • 107
0

Thanks so much to everyone who helped with this. What I ended up doing was:

    string diceSizeString;
    int diceSize;

    cout << "Choose the size of the dice to roll (6, 12 or 20): ";
    cin >> diceSizeString;

    while (diceSizeString != "6" && diceSizeString != "12" && diceSizeString != "20")
    {
        cout << "Invalid number entered. Choose the size of the dice to roll (6, 12 or 20): ";
        cin >> diceSizeString;
    }

    diceSize = stoi(diceSizeString);
AlexDLC
  • 1
  • 1