Don't hardcode the which power of 2 we want to calculate. We'll want to use some smaller values to be sure we get reasonable results we can compute by hand:
int sum_power_digits(int power);
And let's write a main()
that can run it as many times as we need, passing values as program arguments:
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char **argv)
{
while (*++argv) {
char *e;
long l = strtol(*argv, &e, 10);
if (*e || l < 0 || l > INT_MAX) {
fprintf(stderr, "%s: not valid\n", *argv);
continue;
}
int result = sum_power_digits((int)l);
if (!result) {
fprintf(stderr, "%s: FAILED\n", *argv);
continue;
}
printf("sum_digits(2^%s) = %d\n", *argv, result);
}
}
I have made the interface decision that a return value of 0 indicates an error; we could use negative numbers to signal different kinds of error.
Okay, let's move on to the implementation. We can no longer assume a fixed size array to store the digits, so we must allocate what's required. I'm going to save on storage compared to your algorithm, by storing two digits in each element, at the cost of a little more complexity. I'm further saving on storage, as uint8_t
is usually smaller than int
.
#include <stdint.h>
int sum_power_digits(int power)
{
/* We count in base 100; our character type needs to hold up to 198 */
typedef uint8_t digit;
int digits = power / 6 + 1; /* safe upper bound */
digit *d = calloc(digits, sizeof *d);
if (!d) {
fprintf(stderr, "%d: failed to allocate memory\n", power);
return 0;
}
*d = 1; /* start with 2^0 */
int carry = 0;
digit *end = d + digits;
while (power-->0) {
for (digit *p = d; p < end; ++p) {
*p *= 2;
*p += carry;
carry = *p/100;
*p %= 100;
}
}
/* count the decimal digits */
int sum = 0;
for (digit *p = d; p < end; ++p)
sum += *p % 10 + *p / 10;
free(d);
return sum;
}
Notice that I typedeffed the digit type - this worked in my favour when I realised it need to represent up to 198 (needed when doubling 99), and my original choice of char
(which could be signed) might only reach 127.
Remember what I said about testing? Let's have a go:
$ ./126981 1b a -1
1b: not valid
a: not valid
-1: not valid
$ ./126981 `seq 0 9` 1000 2000 262144
sum_digits(2^0) = 1
sum_digits(2^1) = 2
sum_digits(2^2) = 4
sum_digits(2^3) = 8
sum_digits(2^4) = 7
sum_digits(2^5) = 5
sum_digits(2^6) = 10
sum_digits(2^7) = 11
sum_digits(2^8) = 13
sum_digits(2^9) = 8
sum_digits(2^1000) = 1366
sum_digits(2^2000) = 2704
sum_digits(2^262144) = 353041
I'm pretty confident of the single-digit results (that I can do in my head), so I have some faith in the big result. Once we got to 2^7, we demonstrated that carry is working (finally, 2^9 = 512; 5+1+2 = 8).
uint64_t
) then you get away with 36 or 18 long divisions respectively, and you can use the C runtime for converting the 'meta-digits'. \$\endgroup\$