1

I have an issue with the following C++ program. It calculates and prints out the blood alcohol concentration using the Widmark formula based on information the user enters. There is some input error checking, for example if a random character is entered after being asked for which beverage they drank, or if any number higher than 400 is entered after being asked for weight. Then an error message appears (which is intentional) and the user is asked for input again.

However, they are some issues with the input error checking, for example if x>1 characters are entered after being asked for the gender, the error message appears x times (it should appear only once). If any non-integer characters are entered after being asked for weight, the error message appears infinite times, resulting in an endless loop (same issue with litres drunk after entering non-float values).

Is there some way to limit the inputs so that error messages only appear once after any invalid input?

#include<iostream> // for std::cin and std::cout
#include<iomanip> // for std::fixed and std::setprecision

float BAC (char gender, int weight, char drink, float litres) // function to calculate blood alcohol concentration
{
    // local variables
    float bac, rate, vol; // blood alcohol concentration, widmark rate, volume percentage

    // determine widmark rate
    if (gender == 'm' || gender == 'M') // not case-sensitive
    {
        rate = 0.68;
    } 
    if (gender == 'f' || gender == 'F')
    {
        rate = 0.55;
    }
    if (gender == 'c' || gender == 'C')
    {
        rate = 0.75;
    }

    // determine volume percentage
    if (drink == 'b' || drink == 'B') 
    {
        vol = 5;
    } 
    if (drink == 'w' || drink == 'W')
    {
        vol = 12;
    }
    if (drink == 's' || drink == 'S')
    {
        vol = 38;
    }

    // calculating blood alcohol concentration
    bac = ((litres * 1000) * (vol / 100) * 0.8) / (weight * rate);
    // ((litres * 1000) * (vol / 100) * 0.8) --> formula for alcohol mass
    // (litres * 1000) --> conversion to millilitres

    return bac;
}

int main () // main function for user input/output
{
    // variables to be entered
    char gender, drink;
    int weight;
    float litres;

    // getting user input
    std::cout << "Are you male (m), female (f), or a small child (c)?" << std::endl;
    do
    {
        std::cin >> gender;
        if (gender != 'm' && gender != 'M' && gender != 'f' && gender != 'F' && gender != 'c' && gender != 'C') // check input
        {
            std::cout << "\nInvalid input: Please enter 'm', 'f' or 'c' (capital letters are allowed)." << std::endl;
        }
    }
    while (gender != 'm' && gender != 'M' && gender != 'f' && gender != 'F' && gender != 'c' && gender != 'C');
    // repeat as long as valid input is entered

    std::cout << "\nWhat is your weight (in kg)?" << std::endl;
        do
    {
        std::cin >> weight;
        if (weight < 10 || weight > 400)
        {
            std::cout << "\nInvalid input: Please enter a number bigger than or equal to 10 and smaller than or equal to 400." << std::endl;
        }
    }
    while (weight < 10 || weight > 400);

    std::cout << "\nWhich beverage did you drink? beer: (b), wine: (w), schnapps: (s)" << std::endl;
    do
    {
        std::cin >> drink;
        if (drink != 'b' && drink != 'B' && drink != 'w' && drink != 'W' && drink != 's' && drink != 'S')
        {
            std::cout << "\nInvalid input: Please enter 'b', 'w' or 's' (capital letters are allowed)." << std::endl;
        }
    }
    while (drink != 'b' && drink != 'B' && drink != 'w' && drink != 'W' && drink != 's' && drink != 'S');

    std::cout << "\nHow many litres did you drink?" << std::endl;
    do
    {
        std::cin >> litres;
        if (litres < 0 || litres > 10)
        {
            std::cout << "\nInvalid input: Please enter a number bigger than or equal to 0 and smaller than or equal to 10." << std::endl;
        }
    }
    while (litres < 0 || litres > 10);

    // print the result
    std::cout << std::endl << std::endl << "Your blood alcohol concentration is: "
        << std::fixed << std::setprecision(2) // show only 2 decimal places after the dot
        << BAC (gender, weight, drink, litres) // function call
        << " thousandths." << std::endl;

    return 0;
}

1 Answers1

0

Your problem, as I'm sure you've realised, is the 'left over' characters in the std:cin stream after having read each input. Now, although you can't actually "flush" an input stream, you can clear all 'waiting' input characters, using code like the following before each input:

std::cin.clear();
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
std::cin >> weight;

For more discussion on this, see here: How do I flush the cin buffer?

Another point you may need to consider is what happens when any of the input operations fails. You should initialise your variables with 'invalid' values to ensure that the user has actually provided good data:

char gender = '?', drink = '?';
int weight = -1.0;
float litres = -1.0;
Adrian Mole
  • 49,934
  • 160
  • 51
  • 83
  • Thanks, it works perfectly for char variables. Unfortunately the issue remains for int and float. Entering letters still causes an endless loop. –  Feb 01 '20 at 15:10
  • @mister_newbie I can't reproduce that. Have you added the `clear` and `ignore` calls before **every** input? – Adrian Mole Feb 01 '20 at 15:15
  • Actually I added those calls after every `std::cin;`, which has the same effect. I double-checked it and it does work for `int`, but not for `float`. –  Feb 01 '20 at 15:58
  • @mister_newbie See my edited answer - that may help (otherwise, some letters can be interpreted as a float value). – Adrian Mole Feb 01 '20 at 16:03
  • I initialized `float litres = -1.0;` and when I enter characters that aren´t numbers for `litres`, the program always outputs `"0.00"` as the result. I want it to output an error message and ask me to re-enter the value. –  Feb 01 '20 at 16:34