-1

just now I wrote a simple function in C++ that determines if a number is narcissistic or not, and I have stumbled upon something most peculiar. I get the wrong sum in function "IsNumberNarcissistic" for numbers 9800817 and 9926315, which are both narcissistic. The sum I get is always 1 less than it should be, that is 9800816 and 9926314 for these numbers respectively. However, declaring the variable sum as a double solves the problem.

So my question is, what is going on here? Is it Code:Blocks related problem, or something else? I'm using Code:Blocks 13.12. Thank you in advance.

PS. Don't mind those prints I made in the function, I just put them there to see the variable values.

    #include <iostream>
    #include <vector>
    #include <cmath>
    #include <iomanip>

    using std::cin; using std::cout; using std::endl;

    std::vector<int> extractDigits(int n)
    {
        std::vector<int> digits;
        while (n>0)
        {
            digits.push_back(n%10);
            n/=10;
        }
        return digits;
    }

    bool IsNumberNarcissistic(int n)
    {
        auto digits = extractDigits(n);
        int sum(0);
        int power = digits.size();
        for (int digit : digits) sum += std::pow(digit, power);
        for (int digit : digits) cout << std::setprecision(10)     
        << std::pow(digit, power) << endl;
        cout << endl << endl << sum;
        return (sum == n);
    }

    int main()
    {
        IsNumberNarcissistic(9800817);
    }
Shema
  • 111
  • 3
  • 1
    _"Is it Code:Blocks related problem, or something else? "_ Something else I'd say. The IDE is rarely the culprit. More related to target platform and toolchain used. – πάντα ῥεῖ Sep 22 '16 at 18:23
  • Remember that [`std::pow`](http://en.cppreference.com/w/cpp/numeric/math/pow) is a floating point function, and therefore may lead to rounding problems. – Some programmer dude Sep 22 '16 at 18:25
  • 3
    Most likely your problem comes from `sum += std::pow(digit, power)`. It sounds like you may need to learn how to use a debugger to step through your code. With a good debugger, you can execute your program line by line and see where it is deviating from what you expect. This is an essential tool if you are going to do any programming. Further reading: **[How to debug small programs](http://ericlippert.com/2014/03/05/how-to-debug-small-programs/)** – NathanOliver Sep 22 '16 at 18:25
  • My bad, billions are not millions :/ – Rakete1111 Sep 22 '16 at 18:26
  • [Do not use pow() when using integer exponents](http://stackoverflow.com/questions/25678481/why-does-pown-2-return-24-when-n-5-with-my-compiler-and-os). There is no guarantee the answer will be correct, and I wouldn't be shocked if the round-off / truncation issue is revealing itself due to you using `pow()` this way. – PaulMcKenzie Sep 22 '16 at 18:31
  • 1
    Not the problem, but don't use `std::endl` unless you need the extra stuff that it does. `'\n'` ends a line. – Pete Becker Sep 22 '16 at 18:36
  • 1
    @PaulMcKenzie This. Thank you very much. I didn't read the documentation for function pow properly, and that was my biggest mistake here. – Shema Sep 22 '16 at 18:37
  • @Shema -- And as you can [see here](http://ideone.com/jZlSvl), your original code produces the correct results, using `pow()`. See how erratic it could get -- some compilers it will work, but others may have slight rounding errors, as certainly the compiler you're using now has this issue. Stick with computing `pow` using the methods described at the SO link I gave instead of calling `pow()`. The results should now be consistent once you do that. – PaulMcKenzie Sep 22 '16 at 18:40

1 Answers1

0

Commenters already answered your question, so I'm marking this Community Wiki.

I had never heard of Narcissistic numbers, so I made a version of your code which works properly in order to explore the subject a little bit.

std::pow is replaced by my IPow, an integer exponentiation function which uses the standard binary digits/squaring technique.

#include <iostream>
#include <vector>

std::vector<int> ExtractDigits(int n) {
    std::vector<int> digits;
    if(n < 0) { n = -n; }
    do {
        digits.push_back(n % 10);
    } while(n /= 10); // 0 has 1 digit
    return digits;
}

int IPow(int b, int e) {
    if(e < 0) return 0; // truncated fraction
    int s = b;
    int r = 1;
    while(e) {
        if(e % 2) { r *= s; }
        s *= s;
        e /= 2;
    }
    return r;
}

bool IsNumberNarcissistic(int n) {
    const auto digits = ExtractDigits(n);
    int sum = 0;
    const int power = digits.size();
    for (int digit : digits) { sum += IPow(digit, power); }
    for (int digit : digits) { 
        std::cout << digit << "**" << power << " " << IPow(digit, power) << '\n';
    }
    std::cout << "sum: " << sum << '\n';
    return sum == n;
}

int main() {
    for(int n : { 9800817, 9800818, 50 }) {
        std::cout << n << " is" << (IsNumberNarcissistic(n) ? "" : " not") 
            << " Narcissistic\n\n";
    }
}

Outputs

7**7 823543
1**7 1
8**7 2097152
0**7 0
0**7 0
8**7 2097152
9**7 4782969
sum: 9800817
9800817 is Narcissistic

8**7 2097152
1**7 1
8**7 2097152
0**7 0
0**7 0
8**7 2097152
9**7 4782969
sum: 11074426
9800818 is not Narcissistic

0**2 0
5**2 25
sum: 25
50 is not Narcissistic
Christopher Oicles
  • 3,017
  • 16
  • 11