0

I'm writing a program that needs to find abundant, deficient and perfect numbers - according to Greek numerology. What represents an issue here is ensuring that only natural numbers are inserted; more specifically making sure that there are no letters inserted. I tried checking if stream was "broken", if true, print appropriate message. It works, but only once, and I can't figure out why. Here's the code.

 #include <iostream>
 int main ()
{
double n(0);
int sum(0);
int i(0);

std::cout << "Enter a natural number or 0 for exit: ";
std::cin >> n;

int n1 = n;
do {
    for(i = 1; i < n1; i++) {
        if(n1 % i == 0) {
            sum+=i;
        }
    }
    if(!std::cin) {
        std::cout << "Not a natural number!" << std::endl;
        std::cout << "Enter a natural number or 0 for exit: ";
    }
    else if(n != int(n)) {
        std::cout << "Not a natural number!" << std::endl;
        std::cout << "Enter a natural number or 0 for exit: ";
    }
    else if(n1 < 0) {
        std::cout << "Not a natural number!" << std::endl;
        std::cout << "Enter a natural number or 0 for exit: ";
    }
    else if(suma < n1) {
        std::cout << "Number " << n << " is deficient!" << std::endl;
        std::cout << "Enter a natural number or 0 for exit: ";
    }
    else if(suma > n1) {
        std::cout << "Number " << n << " is abudant!" << std::endl;
        std::cout << "Enter a natural number or 0 for exit: ";
    }
    else if(suma == n1) {
        std::cout << "Number " << n << " is perfect!" << std::endl;
        std::cout << "Enter a natural number or 0 for exit: ";
    }
    suma = 0;
    std::cin.clear();
    std::cin.ignore(1000, '\n');
    std::cin >> n;
    n1 = int(n);
} while(n != 0);
std::cout << "Goodbye!";

return 0;
}
genpfault
  • 47,669
  • 9
  • 68
  • 119
Rose
  • 81
  • 8
  • Why do you accept `double`s, only to reject non-integers later on? – Quentin Mar 02 '18 at 17:14
  • It appears to work only when the value is inserted with the `cin` that's outside the loop. – Rose Mar 02 '18 at 17:16
  • @Quentin How else do I check if it's a natural number? It mustn't have any decimal spaces. If I declare it as an `int`, anything that user enters after the `.` won't even be stored in memory, preventing me from checking the nature of a number. – Rose Mar 02 '18 at 17:18
  • How will `n` ever be input a second time if you `std::cin.clear(); std::cin.ignore(1000, '\n'); std::cin >> n;`? How can a user ever time his input between the `.ignore` and subsequent `cin`?? – David C. Rankin Mar 02 '18 at 17:23
  • https://stackoverflow.com/q/4654636/16582 – ravenspoint Mar 02 '18 at 17:26
  • @DavidC.Rankin those two lines will "recover" the stream. – Rose Mar 02 '18 at 17:29
  • @ravenspoint Not allowed to use functions, it's for a college course. We're switching from C to C++, so nothing but basics is allowed for completing the task. – Rose Mar 02 '18 at 17:30
  • Nothing gets more basic than the standard c library – ravenspoint Mar 02 '18 at 17:35
  • 1
    I don't understand, what is the purpose of `n != int(n)`? The `n` variable is already an int. The `cin >> n` will only input an integer. So what is the meaning of `n != int(n)`? – Thomas Matthews Mar 02 '18 at 18:07
  • @ThomasMatthews `n` is a `double`. – Rose Mar 02 '18 at 18:28

2 Answers2

1

The problem appears to be one of logic. You prompt for reinput of n, then

std::cin.clear();
std::cin.ignore(1000, '\n');
std::cin >> n;

That will not "recover" the stream, that will discard the user input of 'n' leaving you stuck. It appears you need to reorder your input and loop to handle the input once per iteration. Something similar to the following:

#include <iostream>

int main (void) {

    double n(0);

    do {
        int sum(0);
        int i(0);

        std::cout << "Enter a natural number or 0 for exit: ";
        std::cin >> n;
        int n1 = n;

        for(i = 1; i < n1; i++)
            if(n1 % i == 0)
                sum+=i;

        if(n != int(n))
            std::cout << "Not a natural number!" << std::endl;
        else if(n1 < 0)
            std::cout << "Not a natural number!" << std::endl;
        else if(sum < n1)
            std::cout << "Number " << n << " is deficient!" << std::endl;
        else if(sum > n1)
            std::cout << "Number " << n << " is abudant!" << std::endl;
        else if(sum == n1)
            std::cout << "Number " << n << " is perfect!" << std::endl;

        sum = 0;

    } while(n != 0);

    std::cout << "Goodbye!\n";

    return 0;
}

Neither .ignore or .clear are necessary. Leading whitespace is ignored for numeric conversion and .clear simply sets 'goodbit' on the stream which has the effect of clearing all error flags. (see std::basic_ios::clear) There is also no real need to check if (!std::cin). It doesn't hurt, it is just superfluous to your code.

Example Use/Output

$ ./bin/inputnumber
Enter a natural number or 0 for exit: 128
Number 128 is deficient!
Enter a natural number or 0 for exit: 30
Number 30 is abudant!
Enter a natural number or 0 for exit: 37
Number 37 is deficient!
Enter a natural number or 0 for exit: 2
Number 2 is deficient!
Enter a natural number or 0 for exit: 1
Number 1 is deficient!
Enter a natural number or 0 for exit: 10
Number 10 is deficient!
Enter a natural number or 0 for exit: 12
Number 12 is abudant!
Enter a natural number or 0 for exit: 0
Number 0 is perfect!
Goodbye!

I'm not 100% clear on your goal, so if I misunderstood, please drop a comment and I'm happy to help further.


Making Your Input Discard Non-Numbers

Rose, after the discussions in the comments, it appears your issue was with n1 being assigned 0 in the event there was an input error (like a cat stepping on the keyboard resulting in ";sdkfgj" being input). That too can be handled, and you were on the right track looking at both .clear and .ignore. Hereto, there is a bit of logic in how you put that together.

When faced with the problem of "How do I make sure the user only enters 'X'?", the normal approach is to loop continually until the user input satisfies the conditions you have set for accepting input. Here, you want a numeric value input. So the approach would be to loop, prompting for input, and then checking the stream state after the user inputs a value. If the stream is in error, you need to .clear() the error, and then .ignore(..., ...) up to the '\n'. (you can use the values in <limits> to disable the character count check and discard whatever number of characters may be present)

This leaves a slight problem. You are looping for input within a loop to begin with. If the user enters 0 to indicate here would like to exit, then you must break both loops to get to "Goodbye!\n";. While you can set flags, etc., the lowly goto is still the standard, and efficient, way to jump out of nested loops.

Putting it altogether, now understanding a bit more about your problem, you could do something similar to the following:

#include <iostream>
#include <limits>

int main (void) {

    double n(0);

    do {
        int sum(0), n1(0);

        while (n1 == 0) {
            std::cout << "Enter a natural number or 0 for exit: ";
            std::cin >> n;
            if (!std::cin) {
                std::cin.clear();
                std::cin.ignore(std::numeric_limits<std::streamsize>::max(),
                                '\n');
            }
            else if (n == 0)
                goto alldone;
            else
                n1 = n;
        }

        for(int i = 1; i < n1; i++)
            if (n1 % i == 0)
                sum += i;

        if(n != int(n))
            std::cout << "Not a natural number!" << std::endl;
        else if(n1 < 0)
            std::cout << "Not a natural number!" << std::endl;
        else if(sum < n1)
            std::cout << "Number " << n << " is deficient!" << std::endl;
        else if(sum > n1)
            std::cout << "Number " << n << " is abudant!" << std::endl;
        else if(n1 && sum == n1)
            std::cout << "Number " << n << " is perfect!" << std::endl;

        sum = 0;

    } while(n != 0);

    alldone:;
    std::cout << "Goodbye!\n";

    return 0;
}

Example Use/Output

$ ./bin/inputnumber
Enter a natural number or 0 for exit: apples
Enter a natural number or 0 for exit: banannas
Enter a natural number or 0 for exit: 128
Number 128 is deficient!
Enter a natural number or 0 for exit: 96
Number 96 is abudant!
Enter a natural number or 0 for exit: foo
Enter a natural number or 0 for exit: 2
Number 2 is deficient!
Enter a natural number or 0 for exit: 0
Goodbye!

Let me know if that is the area that was giving your problems.

David C. Rankin
  • 69,681
  • 6
  • 44
  • 72
  • Yeah, but if you enter a letter, it'll crash. It has to send the same message as it does for `<0` and `!=int(n)` – Rose Mar 02 '18 at 17:59
  • No, it won't, it will assign `0` to `n1` and clear the stream. (try it -- enter `"apples"` for one of the numbers) – David C. Rankin Mar 02 '18 at 18:02
  • @DavidC.Rankin This is true since C++11. Before that, the value was left unchanged, and in this particular case, undefined. – Ilya Popov Mar 02 '18 at 18:13
  • I guess it was my bad for not being clear enough. My sole problem was assignment of `0` to `n1`. – Rose Mar 02 '18 at 18:14
  • You fix that with `else if(n1 && sum == n1)` (if you want to avoid outputting `0` as `perfect`) – David C. Rankin Mar 02 '18 at 18:14
  • Yup, that's pretty much it, I eventually did it, but your solution is a bit more prettier for the eye to look at it. Thanks alot! – Rose Mar 02 '18 at 19:12
  • Sorry it took a while to snap to the part of the right part of the problem that was giving you trouble. Good luck with your coding. – David C. Rankin Mar 02 '18 at 19:13
0

So the only problem was assignment of 0 to n1. When the stream "gets broken", that is it receives input that doesn't corresponds to the variable type, it get's to an undefined state, which has to be cleared with .clear and .ignore, which again, leaves it hanging with 0 value, resulting in ending the loop. My solution is perhaps the most primitive one out there. Just assign a random integer value to n1, and case closed. It will not affect other ifs, as only the first one will be executed, and it will also prevent loop from breaking. So just: if(!(std::cin >> n)) { std::cout << "Not a natural number!" << std::endl; std::cin.clear(); std::cin.ignore(1000, '\n'); n = 1; }

Rose
  • 81
  • 8