Swyamdipta
Swyamdipta

Reputation: 21

What's the problem in the code which was supposed to print Armstrong numbers up to 999

Here is the code written in C language which was supposed to print Armstrong numbers within the given range of 1 - 999. But the code is neither printing anything rather using the CPU resources. Most probably it has got struct in an endless loop. Here is the code -

//C program to print Armstrong numbers up to 999
#include <math.h>
#include <stdio.h>

int main()
{
    int i = 1, b, sum = 0;
    printf("The Armstrong Numbers Within 0-999 are --> ");
    while (i <= 999)
    {
        while (i != 0)
        {
            b = i % 10;
            i = i / 10;
            sum = sum + pow(b, 3);
        }
        if (i == sum)
        {
            printf("%d ", i);
            sum = 0;
        }
        else
        {
            sum = 0;
        }
        i++;
    }

    return 0;
}

Answer without adding any other additional library function is expected.

Upvotes: 1

Views: 222

Answers (1)

Craig Estey
Craig Estey

Reputation: 33621

There were issues with the code ...

The inner while loop trashed the [original] value of i, so the subsequent if would fail because i would always be zero. The inner loop should use a temp variable.

No need to use pow as b * b * b is better/faster and [maybe] more accurate. Using pow seems to violate the criterion:

Answer without adding any other additional library function is expected.

sum = 0; in both the if and the else is redundant. It could be moved below. But, it's better to put it at the start of the outer loop.

Your first printf says that the range is 0-999 but you start your outer loop at 1

The algorithm uses the sum of the cubes per: https://www.javatpoint.com/armstrong-number-in-c

However, Wikipedia https://en.wikipedia.org/wiki/Narcissistic_number states that we should sum the digits to the power of the number of digits in the number (e.g. pow(b,ndig)).

The "cubes" algorithm does not work for numbers with 4 digits.


Here's a refactored version that does sum of the cubes:

//C programme to print Armstrong numbers up to 999

#include <stdio.h>

int
main(void)
{

    int lo = 0;
    int hi = 999;

    printf("The Armstrong Numbers Within %d-%d are --> ",lo,hi);

    for (int i = lo;  i <= hi;  ++i) {
        int sum = 0;

        for (int j = i;  j != 0;  j /= 10) {
            int b = j % 10;
            sum += b * b * b;
        }

        if (i == sum)
            printf("%d ", i);
    }

    printf("\n");

    return 0;
}

Here is the program output:

The Armstrong Numbers Within 0-999 are --> 0 1 153 370 371 407

Here is a version that uses Wikipedia's definition:

//C programme to print Armstrong numbers up to 999

#include <stdio.h>

#ifdef DEBUG
#define dbgprt(_fmt...)     printf(_fmt)
#else
#define dbgprt(_fmt...)     do { } while (0)
#endif

// xpow -- raise to a power
int
xpow(int base,int exp)
{
    int ret = 1;

    dbgprt("xpow: ENTER base=%d exp=%d\n",base,exp);

    while (1) {
        dbgprt("xpow: LOOP base=%d exp=%8.8X\n",base,exp);

        if (exp & 1)
            ret *= base;

        exp >>= 1;
        if (exp == 0)
            break;

        base *= base;
    }

    dbgprt("xpow: EXIT ret=%d\n",ret);

    return ret;
}

int
armstrong(int ndig,int i)
{

    if (ndig == 0) {
        ndig = i ? 0 : 1;
        for (int j = i;  j != 0;  j /= 10, ++ndig);
    }

    int sum = 0;

    for (int j = i;  j != 0;  j /= 10) {
        int b = j % 10;
        b = xpow(b,ndig);
        sum += b;
    }

    int match = (i == sum);

    dbgprt("armstrong: MATCH match=%d i=%d\n",match,i);

    return match;
}

void
armndig(int ndig)
{

    dbgprt("armndig: ENTER ndig=%d\n",ndig);

    int lo = xpow(10,ndig - 1);
    if (lo == 1)
        lo = 0;
    int hi = xpow(10,ndig) - 1;
    printf("The Armstrong Numbers Within %d-%d are -->\n",lo,hi);

    for (int i = lo;  i <= hi;  ++i) {
        if (armstrong(ndig,i))
            printf("ARMSTRONG %d\n",i);
    }

    dbgprt("armndig: EXIT\n");
}

int
main(void)
{

    armstrong(3,407);

    for (int ndig = 1;  ndig <= 4;  ++ndig)
        armndig(ndig);

    return 0;
}

Here is the program output:

The Armstrong Numbers Within 0-9 are -->
ARMSTRONG 0
ARMSTRONG 1
ARMSTRONG 2
ARMSTRONG 3
ARMSTRONG 4
ARMSTRONG 5
ARMSTRONG 6
ARMSTRONG 7
ARMSTRONG 8
ARMSTRONG 9
The Armstrong Numbers Within 10-99 are -->
The Armstrong Numbers Within 100-999 are -->
ARMSTRONG 153
ARMSTRONG 370
ARMSTRONG 371
ARMSTRONG 407
The Armstrong Numbers Within 1000-9999 are -->
ARMSTRONG 1634
ARMSTRONG 8208
ARMSTRONG 9474

UPDATE:

int xpow(int base,int exp) not ready for general use as it has an infinite loop in it when exp < 0. Had code use x%2 rather than x&1, and exp /= 2; rather than exp >>= 1; like the above code that used, %10 and /10, (or an unsigned exp) then this infinite loop would not have been there. – chux - Reinstate Monica

You are correct. I adapted the code from a function I already had that used unsigned arguments.

I had debated whether to use unsigned throughout and use unsigned long long.

Here is a further cleaned up version:

//C programme to print Armstrong numbers up to 999

#include <stdio.h>

#if USE64
typedef unsigned long long num_t;
#define FMT                 "%llu"
#define FMTX                "%16.16X"
#define NDIG                16
#else
typedef unsigned int num_t;
#define FMT                 "%u"
#define FMTX                "%8.8X"
#define NDIG                8
#endif

#ifdef DEBUG
#define dbgprt(_fmt...)     printf(_fmt)
#else
#define dbgprt(_fmt...)     do { } while (0)
#endif

// xpow -- raise to a power
num_t
xpow(num_t base,num_t exp)
{
    num_t ret = 1;

    dbgprt("xpow: ENTER base=" FMT " exp=" FMT "\n",base,exp);

    while (1) {
        dbgprt("xpow: LOOP base=" FMT " exp=" FMTX "\n",base,exp);

#if CHUX
        if (exp % 2)
            ret *= base;
        exp /= 2;
#else
        if (exp & 1)
            ret *= base;
        exp >>= 1;
#endif

        if (exp == 0)
            break;

        base *= base;
    }

    dbgprt("xpow: EXIT ret=" FMT "\n",ret);

    return ret;
}

num_t
armstrong(int ndig,num_t i)
{

    if (ndig == 0) {
        ndig = i ? 0 : 1;
        for (num_t j = i;  j != 0;  j /= 10, ++ndig);
    }

    num_t sum = 0;

    for (num_t j = i;  j != 0;  j /= 10) {
        num_t b = j % 10;
        b = xpow(b,ndig);
        sum += b;
    }

    int match = (i == sum);

    dbgprt("armstrong: MATCH match=%d i=" FMT "\n",match,i);

    return match;
}

void
armndig(int ndig)
{

    dbgprt("armndig: ENTER ndig=%d\n",ndig);

    num_t lo = xpow(10,ndig - 1);
    if (lo == 1)
        lo = 0;
    num_t hi = xpow(10,ndig) - 1;
    printf("The Armstrong Numbers Within " FMT "-" FMT " are -->\n",lo,hi);

    for (num_t i = lo;  i <= hi;  ++i) {
        if (armstrong(ndig,i))
            printf("ARMSTRONG " FMT "\n",i);
    }

    dbgprt("armndig: EXIT\n");
}

int
main(void)
{

    setlinebuf(stdout);

    armstrong(3,407);

    for (int ndig = 1;  ndig <= NDIG;  ++ndig)
        armndig(ndig);

    return 0;
}

Here is the program output for -DUSE64=0:

The Armstrong Numbers Within 0-9 are -->
ARMSTRONG 0
ARMSTRONG 1
ARMSTRONG 2
ARMSTRONG 3
ARMSTRONG 4
ARMSTRONG 5
ARMSTRONG 6
ARMSTRONG 7
ARMSTRONG 8
ARMSTRONG 9
The Armstrong Numbers Within 10-99 are -->
The Armstrong Numbers Within 100-999 are -->
ARMSTRONG 153
ARMSTRONG 370
ARMSTRONG 371
ARMSTRONG 407
The Armstrong Numbers Within 1000-9999 are -->
ARMSTRONG 1634
ARMSTRONG 8208
ARMSTRONG 9474
The Armstrong Numbers Within 10000-99999 are -->
ARMSTRONG 54748
ARMSTRONG 92727
ARMSTRONG 93084
The Armstrong Numbers Within 100000-999999 are -->
ARMSTRONG 548834
The Armstrong Numbers Within 1000000-9999999 are -->
ARMSTRONG 1741725
ARMSTRONG 4210818
ARMSTRONG 9800817
ARMSTRONG 9926315
The Armstrong Numbers Within 10000000-99999999 are -->
ARMSTRONG 24678050
ARMSTRONG 24678051
ARMSTRONG 88593477

Upvotes: 2

Related Questions