1

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.

chqrlie
  • 131,814
  • 10
  • 121
  • 189
Swyamdipta
  • 21
  • 5
  • 1
    You need a *different* `i` in `while (i != 0)`. Copy `i` to another variable and work with that. It's obvious that `i <= 999` will always be `true`, because the next loop sends it to `0`, and then `i++;` sets it to `1`. – Weather Vane Mar 12 '22 at 17:27
  • Note that ```pow()``` returns a double. – Ait-Gacem Nabil Mar 12 '22 at 17:28
  • Tried removing the use of pow() function by replacing it with b * b * b, still getting the same error. – Swyamdipta Mar 12 '22 at 17:30
  • @weather-vane Can you please eleboate. – Swyamdipta Mar 12 '22 at 17:31
  • Look, you have `while (i <= 999)` immediately followed by `while (i != 0)`. Between them set `int j = i` and continue working with `j` from there except where `i` is of the outer loop. You've trashed the outer loop control variable. – Weather Vane Mar 12 '22 at 17:33
  • Thank you, that helped, but with a little correction of using i instead of j in the if statement. Anyway, thank you. – Swyamdipta Mar 12 '22 at 17:44
  • Can we take input from an user. e.g. -printf("How many numbers to you want to work on ? : "); scanf("%d", &a); And then declare an array based on that variable - eg. - int b[a]. Is this correct by the norms of c ? – Swyamdipta Mar 12 '22 at 18:06
  • (There's another issue: Armstrong numbers are equal to the sum of their digits to the power of the total number of digits, but you use the 3rd power throughout; you should use just the digits for single-digit numbers and the squares for numbers from 10-99.) – M Oehm Mar 12 '22 at 18:17
  • Oh, didn't know that earlier. I had just worked with numbers containing 3 digits for that purpose, which is why I always thought that I would only be the matter of cube. Thanks for informing. – Swyamdipta Mar 12 '22 at 18:35
  • And on that front, you should round the value returned by `pow()`. If you can't use `round()`, and all values are positive, I suggest `sum += (int)(pow(b, n) + 0.5);` – Weather Vane Mar 12 '22 at 18:36
  • @MOehm No, I think OP's code using `pow(b,3)` (or `b * b * b`) is correct. From: https://www.javatpoint.com/armstrong-number-in-c _Armstrong number is a number that is equal to the sum of **cubes of its digits**. For example 0, 1, 153, 370, 371 and 407 are the Armstrong numbers._ – Craig Estey Mar 12 '22 at 19:24
  • @MOehm But, this seems to conflict with: https://en.wikipedia.org/wiki/Narcissistic_number – Craig Estey Mar 12 '22 at 19:35
  • @CraigEstey: I think that the "real" definition involves the nth power, but that programming sites tend to focus on three-digit numbers and then turn the mouthful "sum of the digits to the power of the total number of digits" into "sum of the cubes of the digits" for simplicity. But the Java page you link to doesn't do that and does not consider 2 to 9 to be Armstrong numbers. – M Oehm Mar 13 '22 at 11:26
  • ( The [third hit](https://pages.mtu.edu/~shene/COURSES/cs201/NOTES/chap04/arms.html) of my web search (you've linked to the first two) says: "An Armstrong number of three digits is an integer such that the sum of the cubes of its digits is equal to the number itself." The comment in the code already omits "of three digits" and to make the Babylonic confusion perfect, a note below says: "Three-digit numbers are 000, 001, 002, ...".) – M Oehm Mar 13 '22 at 11:28

1 Answers1

2

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
Craig Estey
  • 30,627
  • 4
  • 24
  • 48
  • `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 Mar 12 '22 at 20:41
  • @chux-ReinstateMonica Yes, you are correct. I did a cut-and-paste from code I had that used `unsigned` numbers. I've updated my answer. – Craig Estey Mar 12 '22 at 21:38
  • Although `xpow()` is a side issue, I like `int ipow(int base, int expo)` with most `ipow(base, negative)` returning 0 like `ipow(2, -2)` and `INT_MAX`, `errno = ERANGE` for `ipow(2, 1000)`. UV – chux - Reinstate Monica Mar 12 '22 at 22:23