How do I properly convert to any base? - c

My code compiled successfully, but any time I run the argument ./base.o 42 2, which converts it to base 2 in binary, the output is 0, and anytime I run ./base.o 99 5, the result is 444 repeatings.
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
int main(int argc, char *argv[]) {
int remainder = 0;
int num0 = 0;
int i = 0;
int num1 = atoi(argv[1]);
int num2 = atoi(argv[2]);
int quotient = num1;
while (quotient != 0) {
remainder = num1 % num2;
quotient = quotient / num2;
num0 = (remainder * pow(10, i)) + num0;
i++;
}
printf("%d\n", num0);
return 0;
}

Number base is just for humans to read. A number is a shorthand way of writing polynomial of increasing powers of the radix.
For example, the number 3057 with base (== radix) 10 is:
      3×103  +  0×102  +  5×101  +  7×100
I can totally change the radix:
      B×162  +  F×161  +  1×160
We see that B (11)×16×16 plus F (15)×16 plus 1 is 2816+240+1 = 3057 — the very same number as before.
Text to Integer
Converting from the text representation to a numeric value is easy:
int value = 0;
for (char * s = text_number; *s; s++)
value = (value * radix) + value_of( *s );
That “value of c” thing is just to say that '7' should be transformed to a 7 and things like 'E' should be transformed to 15 (since the ASCII representation of a digit != that digit). This kind of thing is easiest done with a lookup table.
Integer to Text
Going the other way is almost as easy, you just need to print the digits in the right order. It helps to have a string to hold those digits.
char buffer[ N ] = { 0 }; // where N is big enough to hold all the digits
char * s = buffer + N - 1; // start at the back
while (value)
{
*(--s) = digit_for( value % radix );
value /= radix;
}
puts( s );
Again, that “digit for” converts values like 7 to '7' and 15 to 'E'. Again a simple lookup table (the very same one as before, actually) is useful here.
If value is 0 we should just puts( "0" ) and avoid the loop.
Edge Cases
Alas, in computing, life is never as simple as we would like it to be. We need to deal with things like negative numbers and INT_MIN and zeros and stuff.
For what you are doing, I wouldn’t worry about it. But if it matters, you can keep a textual representation of INT_MIN (or whatever data type you are working against) to compare against the input, and assign that value directly if the input matches it.
You can even generate this minimum value representation by converting the largest possible value, adding one to the last character in the string, and slapping a minus sign on the front of it.
Of course, if you are working over an unsigned type, you don’t need to bother.
You still need to be aware of overflow, though. If you get more digits than your resulting type can hold...
And we see where the weeds makes this stuff get kind of tricky. The basic idea is simple, just make sure to handle all the weird edge cases where stuff goes wonky.

There are multiple issues in the code:
the program arguments seem to be expressed in base 10 and the output must be produced in the base corresponding to the second argument.
for this, the conversions int num1 = atoi(argv[1]); and int num2 = atoi(argv[2]); are correct, assuming there are at least 2 arguments: you should test the actual number of arguments received by the program.
but the computation in the loop is incorrect because you use remainder = num1 % num2; instead of remainder = quotient % num2; so you always get the last digit replicated.
your approach is very limited as you can only handle bases up to 10 and numbers with at most 10 digits in the target base.
you should instead store the remainders in an array and output these remainders as digits in reverse order.
Here is a modified version:
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[]) {
int remainder[sizeof(int) * CHAR_BIT];
int num1, num2, quotient, i;
const char *digits = "0123456789abcdefghijklmnopqrstuvwxyz";
if (argc > 2) {
num1 = atoi(argv[1]);
num2 = atoi(argv[2]);
if (num2 < 2 || num2 > 36) {
printf("invalid base: %d\n", num2);
return 1;
}
quotient = num1;
i = 0;
/* using a for(;;) loop instead of `while (quotient != 0)` to
produce at least 1 digit for quotient == 0
*/
for (;;) {
/* using abs to handle negative numbers all the way to INT_MIN */
remainder[i++] = abs(quotient % num2);
quotient = quotient / num2;
if (quotient == 0)
break;
}
if (num1 < 0) {
putchar('-');
}
while (i > 0) {
putchar(digits[remainder[--i]]);
}
putchar('\n');
}
return 0;
}

This looked like a fun question to work on, and I see during the time I was refactoring the initial program that other good alternatives have been offered up. To that mix, I offer up this refactored version of the base conversion program.
#include <stdio.h>
#include <stdlib.h>
#define MAX 100
char derive(int digit)
{
char value;
if (digit < 10)
{
value = (char)digit + '0';
}
else
{
switch(digit)
{
case 10:
value = 'A';
break;
case 11:
value = 'B';
break;
case 12:
value = 'C';
break;
case 13:
value = 'D';
break;
case 14:
value = 'E';
break;
case 15:
value = 'F';
break;
default:
value = '*';
break;
}
}
return value;
}
int main(int argc, char *argv[]) {
char cnv[MAX];
int remainder = 0;
int num0 = 0;
int i = 0;
int num1 = atoi(argv[1]);
int num2 = atoi(argv[2]);
int quotient = num1;
if (num2 < 2 || num2 > 16)
{
printf("Currently, only base numbers from 2 to 16 are allowed\n");
return 0;
}
num0 = num2;
for (int j = 0; j < MAX; j++) /* Initialize the character array to terminator values */
{
cnv[j] = '\0';
}
while (1) /* Determine the largest power of the base needed for deriving a converted value */
{
num0 = num0 * num2;
if (num0 > num1)
{
num0 = num0 / num2;
break;
}
}
while (num0 >= num2) { /* Work down from the highest base power to derive the base value */
remainder = num1 % num0;
quotient = quotient / num0;
cnv[i] = derive(quotient);
quotient = remainder;
num0 = num0 / num2;
i++;
}
cnv[i] = derive(remainder); /* Finish by storing the "units" value */
printf("%s\n", cnv);
return 0;
}
A few bits of information to point out as to what I did to make the base conversion function.
Instead of utilizing the power function within the "math.h" include file, the program just keeps multiplying the base value by itself until the largest power of the base is reached that is less than or equal to the number being evaluated.
Once the largest power is determined, that value is used to derive each digit value by performing an integer division and retaining the remainder for subsequent divisions by decreasing powers of the base number.
Each quotient value is converted to its equivalent digit in the base and then stored in a character array and the divisor is divided by the base number until the divisor value is less than the base value.
Once all division has occurred within the "while" loop, the final remainder value is converted to the final digit and placed into the character array and the character array is then printed presenting the converted value.
So instead of working up with the power function, this method derives the needed largest power of the base value needed and works its way back down through the division and decrement process.
Testing out the program against the original values presented in the question produced the following terminal output.
#Vera:~/C_Programs/Console/Base/bin/Release$ ./Base 42 2
101010
#Vera:~/C_Programs/Console/Base/bin/Release$ ./Base 99 5
344
Anyway, you might want to evaluate this refactored version along with the other presented solutions to see if it meets the spirit of your project.

Related

My decimal to hex conversion function only woks with positive nums

I'm having problems converting negative numbers, from decimal base to hexadecimal base, with the following function:
#include <stdio.h>
int main()
{
int quotient, remainder;
int i, j = 0;
char hexadecimalnum[100];
quotient = -50;
while (quotient != 0)
{
remainder = quotient % 16;
if (remainder < 10)
hexadecimalnum[j++] = 48 + remainder;
else
hexadecimalnum[j++] = 55 + remainder;
quotient = quotient / 16;
}
strrev(hexadecimalnum);
printf("%s", hexadecimalnum);
return 0;
}
For quotient = -50; the correct output should be:
ffffffce
But this function's output is:
.
With positive numbers the output is always correct but with negative numbers not.
I'm having a hard time understanding to why it doesn't work with negative numbers.
Some fixes:
unsigned int quotient - you need to convert -50 to a large hex number in two's complement or you'll get the wrong number of iterations (2) in the loop, instead of 8 as required.
Removal of "magic numbers": '0' + remainder and 'A' + remainder - 10.
Zero initialize hexadecimalnum becaues it needs to be null terminated before printing a string from there. Better yet, add the null termination explicitly.
Use for loops when possible.
Might as well store the characters from the back to front and save the extra call of reversing the string.
Result:
#include <stdio.h>
// 4 bytes*2 = 8 nibbles
#define HEX_STRLEN (sizeof(int)*2)
int main()
{
unsigned int remainder;
int i = 0;
char hex[100];
for(unsigned int q = -50; q!=0; q/=16)
{
remainder = q % 16;
if (remainder < 10)
hex[HEX_STRLEN-i-1] = '0' + remainder;
else
hex[HEX_STRLEN-i-1] = 'A' + remainder - 10;
i++;
}
hex[HEX_STRLEN] = '\0'; // explict null termination
printf("%s\n", hex);
}
(There's lots of improvements than can be made still, this is just to be considered as the first draft.)
You can use printf's format specifier "%08x", then you can print any number in their respective hexadecimal representation.
#include <stdio.h>
void num_to_hex(int a, char *ptr) { snprintf(ptr, 9, "%08x", a); }
int main() {
char hex[10] = {};
num_to_hex(-50, hex);
printf("%s\n", hex);
return 0;
}
Output:
ffffffce

finding how many times number2 is showing in number1

I am solving an exercise in C and I got stuck. I don't know the logic of the code to get to my solution. For example we enter 2 numbers from input let the numbers be 123451289 and 12 and I want to see how many times number 2 is showing at number 1 (if this is confusing let me know). For the numbers earlier the program outputs 2. I tried solving it here is my code:
#include <stdio.h>
int main() {
int num1, num2, counter = 0;
scanf("%d%d", num1, num2);
if (num1 < num2) {
int temp = num1;
num1 = num2;
num2 = temp;
}
int copy1 = num1;
int copy2 = num2;
while (copy2 > 0) {
counter++; // GETTING THE LENGHT OF THE SECOND NUMBER
copy2 /= 10;
// lastdigits = copy1 % counter //HERE I WANT TO GET THE LAST DIGITS OF THE FIRST NUMBER
// But it does not work
}
}
My question is how can I get the last digits of the first number according to the second one for example if the second number have 3 digits I want to get the last 3 digits of the first number. For the other part I think I can figure it out.
I must solve this problem WITHOUT USING ARRAYS.
The problem: find all the needles (e.g. 12) in a haystack (e.g. 123451289).
This can be done simply without arrays using a modulus of the needle. For 12, this is 100. That is, 12 is two digits wide. Using the modulus, we can
isolate the rightmost N digits of the haystack and compare them against the needle.
We "scan" haystack repeatedly by dividing by 10 until we reach zero.
Here is the code:
#include <stdio.h>
int
main(void)
{
int need, hay, counter = 0;
scanf(" %d %d", &hay, &need);
// ensure that the numbers are _not_ reversed
if (hay < need) {
int temp = need;
need = hay;
hay = temp;
}
// get modulus for needle (similar to number of digits)
int mod = 1;
for (int copy = need; copy != 0; copy /= 10)
mod *= 10;
// search haystack for occurences of needle
// examine the rightmost "mod" digits of haystack and check for match
// reduce haystack digit by digit
for (int copy = hay; copy != 0; copy /= 10) {
if ((copy % mod) == need)
++counter;
}
printf("%d appears in %d exactly %d times\n",need,hay,counter);
return 0;
}
UPDATE:
I'm afraid this does not work for 10 0. –
chqrlie
A one line fix for to the modulus calculation for the 10/0 case. But, I've had to add a special case for the 0/0 input.
Also, I've added a fix for negative numbers and allowed multiple lines of input:
#include <stdio.h>
int
main(void)
{
int need, hay, counter;
while (scanf(" %d %d", &hay, &need) == 2) {
counter = 0;
// we can scan for -12 in -1237812
if (hay < 0)
hay = -hay;
if (need < 0)
need = -need;
// ensure that the numbers are _not_ reversed
if (hay < need) {
int temp = need;
need = hay;
hay = temp;
}
// get modulus for needle (similar to number of digits)
int mod = need ? 1 : 10;
for (int copy = need; copy != 0; copy /= 10)
mod *= 10;
// search haystack for occurences of needle
// examine the rightmost "mod" digits of haystack and check for match
// reduce haystack digit by digit
for (int copy = hay; copy != 0; copy /= 10) {
if ((copy % mod) == need)
++counter;
}
// special case for 0/0 [yecch]
if ((hay == 0) && (need == 0))
counter = 1;
printf("%d appears in %d exactly %d times\n", need, hay, counter);
}
return 0;
}
Here is the program output:
12 appears in 123451289 exactly 2 times
0 appears in 10 exactly 1 times
0 appears in 0 exactly 1 times
UPDATE #2:
Good fixes, including tests for negative numbers... but I'm afraid large numbers still pose a problem, such as 2000000000 2000000000 and -2147483648 8 –
chqrlie
Since OP has already posted an answer, this is bit like beating a dead horse, but I'll take one last attempt.
I've changed from calculating a modulus of needle into calculating the number of digits in needle. This is similar to the approach of some of the other answers.
Then, the comparison is now done digit by digit from the right.
I've also switched to unsigned and allow for the number to be __int128 if desired/supported with a compile option.
I've added functions to decode and print numbers so it works even without libc support for 128 bit numbers.
I may be ignoring [yet] another edge case, but this is an academic problem (e.g. we can't use arrays) and my solution is to just use larger types for the numbers. If we could use arrays, we'd keep things as strings and this would be similar to using strstr.
Anyway, here's the code:
#include <stdio.h>
#ifndef NUM
#define NUM long long
#endif
typedef unsigned NUM num_t;
FILE *xfin;
int
numget(num_t *ret)
{
int chr;
num_t acc = 0;
int found = 0;
while (1) {
chr = fgetc(xfin);
if (chr == EOF)
break;
if ((chr == '\n') || (chr == ' ')) {
if (found)
break;
}
if ((chr >= '0') && (chr <= '9')) {
found = 1;
acc *= 10;
chr -= '0';
acc += chr;
}
}
*ret = acc;
return found;
}
#define STRMAX 16
#define STRLEN 100
const char *
numprt(num_t val)
{
static char strbuf[STRMAX][STRLEN];
static int stridx = 0;
int dig;
char *buf;
buf = strbuf[stridx++];
stridx %= STRMAX;
char *rhs = buf;
do {
if (val == 0) {
*rhs++ = '0';
break;
}
for (; val != 0; val /= 10, ++rhs) {
dig = val % 10;
*rhs = dig + '0';
}
} while (0);
*rhs = 0;
if (rhs > buf)
--rhs;
for (char *lhs = buf; lhs < rhs; ++lhs, --rhs) {
char tmp = *lhs;
*lhs = *rhs;
*rhs = tmp;
}
return buf;
}
int
main(int argc,char **argv)
{
num_t need, hay, counter;
--argc;
++argv;
if (argc > 0)
xfin = fopen(*argv,"r");
else
xfin = stdin;
while (1) {
if (! numget(&hay))
break;
if (! numget(&need))
break;
counter = 0;
// we can scan for -12 in -1237812
if (hay < 0)
hay = -hay;
if (need < 0)
need = -need;
// ensure that the numbers are _not_ reversed
if (hay < need) {
num_t temp = need;
need = hay;
hay = temp;
}
// get number of digits in needle (zero has one digit)
int ndig = 0;
for (num_t copy = need; copy != 0; copy /= 10)
ndig += 1;
if (ndig == 0)
ndig = 1;
// search haystack for occurences of needle
// starting from the right compare digit-by-digit
// "shift" haystack right on each iteration
num_t hay2 = hay;
for (; hay2 != 0; hay2 /= 10) {
num_t hcopy = hay2;
// do the rightmost ndig digits match in both numbers?
int idig = ndig;
int match = 0;
for (num_t need2 = need; idig != 0;
--idig, need2 /= 10, hcopy /= 10) {
// get single current digits from each number
int hdig = hcopy % 10;
int ndig = need2 % 10;
// do they match
match = (hdig == ndig);
if (! match)
break;
}
counter += match;
}
// special case for 0/0 et. al. [yecch]
if (hay == need)
counter = 1;
printf("%s appears in %s exactly %s times\n",
numprt(need), numprt(hay), numprt(counter));
}
return 0;
}
Here's the program output:
12 appears in 123451289 exactly 2 times
123 appears in 123451289 exactly 1 times
1234 appears in 123451289 exactly 1 times
1 appears in 123451289 exactly 2 times
0 appears in 10 exactly 1 times
0 appears in 0 exactly 1 times
1000000000 appears in 1000000000 exactly 1 times
2000000000 appears in 2000000000 exactly 1 times
This looks along the lines of what you're attempting.
You can use the pow() function from math.h to raise 10 to the power of how many digits you need for your modulus operation.
Compile with -lm or make your own function to calculate 10^num_digits
#include <stdio.h>
#include <math.h>
int main() {
int x = 123456789;
double num_digits = 3.0;
int last_digits = x % (int)pow(10.0, num_digits);
printf("x = %d\nLast %d Digits of x = %d\n", x, (int)num_digits, last_digits);
return 0;
}
Outputs:
x = 123456789
Last 3 Digits of x = 789
I think you are trying to ask :- if number1 = 1234567 and number2 = 673, then, length of number2 or number2 has 3 digits, so, you now want the last 3 digits in number1, i.e, '456', if I'm not wrong.
If that is the case, then, what you did to find the number of digits in num2 is correct, i.e,
while (copy2>0) {
counter++; // GETTING THE LENGHT OF THE SECOND NUMBER
copy2/=10;
}
you can do the same for number1 and find out its number of digits, then you can compare whether the number of digits in number2 is less than that in number1. Ex, 3 is less than number of digits in number1, so you can proceed further. Let's say number of digits in number1 is 7 and you want the last 3 digits, so you can do iterate over the digits in number1 till count of digits in number2 and pop out each last digit and store them in an array.
The code:
#include <stdio.h>
int main()
{
int num1,num2;
int count1 = 0, count2 = 0;
scanf("%d",&num1);
scanf("%d",&num2);
if(num1<num2){
int temp = num1;
num1 = num2;
num2 = temp;
}
int copy1 = num1;
int copy2 = num2;
while (copy1>0)
{
count1++;
copy1/=10;
}
while (copy2>0)
{
count2++;
copy2/=10;
}
// printf("num1 has %d digits and num2 has %d digits\n", count1, count2);
if (count1 >= count2)
{
int arr[count2];
int x = count2;
int p = num1;
int i = 0;
while (x > 0)
{
arr[i++] = p%10;
x --;
p/=10;
}
for (int j = 0; j < i; j++)
{
printf("%d ", arr[j]);
}
}
return 0;
}
output : 8 7 6
let's say, num1 = 12345678, num2 = 158, then arr = {8,7,6}.
You must determine the number of digits N of num2 and test if num1 ends with num2 modulo 10N.
Note these tricky issues:
you should not sort num1 and num2: If num2 is greater than num1, the count is obviously 0.
num2 has at least 1 digit even if it is 0.
if num1 and num2 are both 0, the count is 1.
if num2 is greater then INT_MAX / 10, the computation for mod would overflow, but there can only be one match, if num1 == num2.
it is unclear whether the count for 1111 11 should be 2 or 3. We will consider all matches, including overlapping ones.
to handle larger numbers, we shall use unsigned long long instead of int type.
Here is a modified version:
#include <limits.h>
#include <stdio.h>
int main() {
int counter = 0;
unsigned long long num1, num2;
if (scanf("%llu%llu", &num1, &num2) != 2) {
printf("invalid input\n");
return 1;
}
if (num1 == num2) {
/* special case for "0 0" */
counter = 1;
} else
if (num1 > num2 && num2 <= ULLONG_MAX / 10) {
unsigned long long copy1 = num1;
unsigned long long mod = 10;
while (mod < num2) {
mod *= 10;
}
while (copy1 > 0) {
if (copy1 % mod == num2)
counter++;
copy1 /= 10;
}
}
printf("count=%d\n", counter);
return 0;
}
Note that leading zeroes are not supported in either number: 101 01 should produce a count of 1 but after conversion by scanf(), the numbers are 101 and 1 leading to a count of 2. It is non trivial to handle leading zeroes as well as numbers larger than ULLONG_MAX without arrays.
This was the answer that i was looking for but thank you all for helping :)
#include <stdio.h>
#include <math.h>
int main(){
int num1,counter1,counter2,num2,temp,digit,copy1,copy2;
scanf("%d%d",&num1,&num2);
if(num1<num2){
temp = num1;
num1 = num2;
num2 = temp;
}
copy1 = num1;
copy2 = num2;
counter1 = counter2 = 0;
while (copy2>0) {
counter1++;
copy2/=10;
}
counter1 = pow(10,counter1);
if(num1> 1 && num2>1)
while (copy1>0) {
digit = copy1%counter1;
if(digit==num2){
counter2++;
}
copy1/=10;
} else{
if(num2<1){
while (copy1>0) {
digit = copy1%10;
if(digit==copy2){
counter2++;
}
copy1/=10;
}
}
}
printf("%d",counter2);
}

C converting 20 digit string to number for IBAN validation

I am doing an IBAN validation in C. For this I have a char* which is something like '2012129431327715102998'.
Now I want to check the IBAN by taken the value modulo 97.
So I want to do 2012129431327715102998 % 97.
I have already tried to convert the char* with strtoull but this gives me an out-of-range error. So my question is: How can I convert this char* to a number where I can do a modulo calculation? Thanks in advance
You can write a custom function for this. Applying the modulo operator on partial sums, you can convert a number of arbitrary length:
#include <stdio.h>
int mod97(const char *s) {
int res = 0;
while (*s >= '0' && *s <= '9') {
res = (res * 10 + (*s++ - '0')) % 97;
}
return res;
}
int main(int argc, char *argv[]) {
for (int i = 1; i < argc; i++) {
printf("%s -> %d\n", argv[i], mod97(argv[i]));
}
return 0;
}
Output:
./mod97 2012129431327715102998
2012129431327715102998 -> 53
This method is simpler and more generic than the one described in the wiki article: computing the modulo 97 of a large number can be achieved by splitting the number in chunks of 9 digits and combining the modulo of these chunks. This splitting is specific to 97 and works because 1000000000 % 97 == 1. The above method works for any modulo value up to INT_MAX / 10.
A simple way without using additional library is to remember that mathematically: mod(a*b, c) == mod(b * mod(a, c), c). So you can process the number in chunks:
// suitable for a 32 bits system, can use 8 for a 64 bits one
#define NB 4
/*********************
* Initial is a string containin only digits representing an arbitrary large number
* div in a number < 10000 (because NB is 4)
* ******************/
int large_mod(char *initial, int div) {
char old[1 + (NB * 2)] = ""; // enough room for a remainder and next chunk
long val;
for (unsigned i=0; i<strlen(initial); i+= NB) {
strncat(old, initial + i, NB); // add the new chunk
val = atol(old) % div; // compute the remainder
sprintf(old, "%ld", val); // keep it for next chunk
// printf("%ld ", val); // uncomment for debugging
}
return (int) val;
}
For 2012129431327715102998 % 97, it gives as expected 53.

How do I multiply a long integer with different numbers in C program?

I am very new to C programming and I am writing a program which takes a number which is suppose to be 9 digits long. After this I multiply each digit with either 1 or 2. I am using arrays to ask user to enter their numbers. I would like to know if there is a way to multiply those 9 numbers with different numbers as one integer instead of using arrays? Here is my code with arrays:
#include <stdio.h>
int main(void) {
int sin_num[9];
int num1;
int num2, num11, num12;
int num3, num4, num5, num6, num7, num8, num9, num10;
for(num1=0; num1<9; num1++) {
printf("Enter your SIN number one by one:");
scanf("%d", &sin_num[num1]);
}
num2 = sin_num[0] * 1;
num3 = sin_num[1] * 2;
num4 = sin_num[2] * 1;
num5 = sin_num[3] * 2;
num6 = sin_num[4] * 1;
num7 = sin_num[5] * 2;
num8 = sin_num[6] * 1;
num9 = sin_num[7] * 2;
num10 = sin_num[8] * 1;
Right now I am doing this:
element 1 * 1
element 2 * 2
element 3 * 1
But how can I do, lets say if I enter 123456789 multiply with different numbers:
123456789
121212121
Well I couldn't much understand what you were asking. Anyways hope this is what you are looking for.....
#include<stdio.h>
int main()
{
long int nine_digit_num;
int step=100000000;
int digit,input_num,i;
printf("Enter 9 digit number:\n");
scanf("%ld",&nine_digit_num);
for(i=1;i<=9;i++)
{
printf("Enter a number to multiply with the %d digit:\n",i);
scanf("%d",&input_num);
digit=nine_digit_num/step; // this and the next step are used to
digit=digit%10; // obtain the individual digits.
printf("%d*%d=%d\n",digit,input_num,digit*input_num);
step=step/10;
}
return 0;
}
I'm sure there are Luhn algorithm solutions already written that you could reference, but I'm going to invent my own right now just to have a walkthrough.
Since your input is only 9 digits, it will fit in a plain 32 bit variable. I'll use unsigned on the assumption it's 32 bits or bigger, but for production code, you'd likely want to use inttypes.h uint32_t and associated scanf macros.
#include <stdio.h>
int main(void) {
unsigned sin_num, checksum, digit;
int i;
printf("Enter your SIN as a 9 digit number using only digits:\n");
if (scanf(" %9u", &sin_num) < 1) ... do error handling or just exit ...
for (i = 0; sin_num; ++i) {
digit = sin_num % 10;
sin_num /= 10;
if (i & 1) { // Double odd digits (might have this backwards; check me for your case
digit *= 2;
if (digit >= 10) digit = digit % 10 + digit / 10; // Luhn carry is strange
}
checksum += digit;
}
... do whatever else you need to do ...
It's not a single mathematical operation because Luhn's carry is too weird for magic number tricks, but it's still much more straightforward than a bunch of single digit scanf calls and array storage.

Convert index to sequence from custom alphabet

I would like to produce a function which takes an integer x and char array in, and returns a string x steps into the sequence.
For example, consider the alphabet 'abc', which would produce the strings a, b, c, aa, ab, ac, ba, bb, bc, ca, cb, cc, aaa, aab... If the index 0 was passed in, I would expect the output to be 'a'; likewise, if the index 34 was passed in, I would expect the output 'cbb'.
For the alphabet '0123456789' I would expect the strings 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 00, 01, 02, 03, 04, 05, 06, 07, 08, 09, 10, 11...
I have written the following thus far, but am getting stuck on cases 21-23, 33-35, 45-47 where the behaviour deviates and I've been staring at this for a number of hours now without a pattern jumping out at me (with respect to the alphabet size and index). At first I didn't notice the issue, using a larger sized alphabet until it created bigger issues further in my program.
I'm not going to pretend the code below is in anyway elegant, following good practice, nor optimised - at this stage I really just want to understand the correct implementation of this pattern and have been changing things all over the place to attempt to resolve the issue. Apologies in advance if the variable names are confusing. Also, is this a common pattern/issue? I have tried to search for similar algorithms but have been unable to find anything with the terms that come to mind.
unsigned long power(int num, int exp)
{
int i;
unsigned long ret = num;
if (exp == 0) return 1;
for (i = 1; i < exp; i++)
{
ret *= num;
}
return ret;
}
unsigned long sumsqr(int base, int exp)
{
unsigned long sum;
for (sum = 0; exp > 0; exp--)
{
sum += power(base, exp);
}
return sum;
}
char * generateStringT(unsigned long index, char * charmap)
{
unsigned long scaler;
unsigned long remainder;
unsigned long divisor;
int base;
int exponent;
int factor;
char * buffer;
char * string;
int i;
buffer = malloc(sizeof(char) * 100);
i = 0;
base = strlen(charmap);
exponent = 0;
divisor = 0;
remainder = index;
while(sumsqr(base, exponent) <= index)
{
exponent++;
}
exponent--;
factor = exponent;
while(factor >= 0)
{
divisor = power(base, factor);
if ((factor > 1) && (exponent > 0))
divisor += power(base, factor-1);
scaler = remainder/divisor;
remainder = remainder - scaler * divisor;
printf("%lu,", scaler);
if ((factor == exponent) && (exponent > 0)) scaler--;
buffer[i++] = charmap[scaler];
factor--;
}
buffer[i++] = '\0';
string = malloc((strlen(buffer) + 1) * sizeof(char));
strcpy(string, buffer);
free(buffer);
return string;
}
What you are trying to do there looks like a base conversion, but actually is slightly different. Any number in any base can be thought as if they have infinitely many preceding zeros (or whatever the least significant digit is at that base) behind the represented number. This is not true in your case.
In your case, you lay importance to the amount of digits on the number you represent, making it slightly more complicated to index them. With bases in maths, it is easy to calculate the index of a represented number in any base b; that is, sum of the rank times the base raised to the power of order for each digit. In your case, the index builds up an additional sum_{k = 1}^{amount.of.digits.on.our.number - 1} base^k. If we subtract that addition from the index, our task becomes rather easy.
That addition can be calculated using your sumsqr function.
Here, I have changed your code just a little, with comments at where I've done changes, which is able to resolve many, just like you expect it to:
// added this
remainder -= sumsqr(base, exponent);
while (factor >= 0)
{
divisor = power(base, factor);
// commented this out
// if ((factor > 1) && (exponent > 0))
// divisor += power(base, factor - 1);
scaler = remainder/divisor;
remainder = remainder - scaler * divisor;
printf("%lu,", scaler);
// commented this out
// if ((factor == exponent) && (exponent > 0))
// scaler--;
buffer[i++] = charmap[scaler];
factor--;
}
I am not exactly sure what you were trying to do with the parts I've commented out. My guess is that you were trying to increase the divisor by that amount of difference I've talked previously, instead of decreasing the index or remainder by that amount.
Hope this helps in any way.
Not a fix (at a glance, your code uses a similar idea -- but more complicated!), but this is the code I used to convert an integer index to an a,b,c-format page number:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
char *number_alpha (char *dest, int value, char *base)
{
char *ddest = dest, *startdest = dest, swop;
if (value < 0)
{
value = -value;
*dest = '-';
startdest++;
ddest++;
}
value++;
do
{
*ddest = base[((value-1) % strlen(base))];
ddest++;
value = (value-1)/strlen(base);
} while (value > 0);
*ddest = 0;
ddest--;
while (ddest > startdest)
{
swop = *ddest;
*ddest = *startdest;
*startdest = swop;
startdest++;
ddest--;
}
return dest;
}
int main (int argc, char **argv)
{
int number;
char result[256];
if (argc != 3)
{
printf ("usage: [number] [string]\n");
return -1;
}
number = strtol (argv[1], NULL, 10);
number_alpha (result, number, argv[2]);
printf ("%d in 'base' %s yields %s\n", number, argv[2], result);
return 0;
}
It is very similar to the common task 'convert an integer to decimal notation'. By removing the value++ and changing (value-1) twice to just value in number_alpha, you get a bog-standard Int-To-Ascii routine. This one is special because the "wrap" occurs at a different place: for a base of 0123456789, incrementing 9 shows 00, not 10.
Sample outputs:
0 in 'base' abc yields a
34 in 'base' abc yields cbb
34 in 'base' 0123456789 yields 24
-34 in 'base' abc yields -cbb
9 in 'base' 0123456789 yields 9
10 in 'base' 0123456789 yields 00
--
See Translate a column index into an Excel Column Name for a couple of implementations in other languages. They seem to focus on recursive solutions, where mine is linear (for better or worse).

Resources