Write a program in C to apply Luhn's algorithm for credit card validation

Mynah picture Mynah · Oct 12, 2016 · Viewed 18.9k times · Source

I am completely new to C (and all forms of programing...) and following the CS50 class this year. I'm having a really hard time with writing a simple program that uses Luhn's algorithm to test the validity of credit card numbers.

I need my program to prompt a user for an input and re-prompt in case the input doesn't follow a credit card format (ex: negative numbers or letters, etc...) and then apply the algorithm to see if the number is a valid credit card number and if yes, whether it's Visa, MasterCard or AmEx.

I know that this question has been answered with different codes on this website, I swear I read everything that I could possibly find (on this site and elsewhere on the net) but I'm having a really hard time understanding the C syntax and I wanted to try to come up with something myself instead of copying bits of codes I don't understand from other answers. If someone can help me out and look at what I've done so far and tell me what I'm doing wrong I would be really grateful. Also, any tips that could help me make sense of the C syntax logic better would be extremely appreciated, again I'm a totally and utter newbie (3 weeks into studying programming on a self-paced basis...) .

My program is compiling but when I run it it's acting up in a very weird way: when I enter an input sometimes it will say that it is invalid (even if it's a valid number) and sometimes it will just not return anything after I press enter and won't stop running no matter how many times I press the return key.

Here is my code so far:

#include <stdio.h>
#include <cs50.h>
#include <math.h>

int main(void)
{
    printf("Please give me your credit card number:\n") ;

    long long card_num ;

    do
    {
        card_num = GetLongLong() ;
    }
    while (card_num < 1 || card_num > 9999999999999999) ;

    // Make a copy of the card number to be used and modified throughout the process.

    long long temp_num = card_num ;
    int digit = 0 ;
    int count = 0 ;
    int sum_a = 0 ;
    int sum_b = 0 ;

    // Isolate every digit from the credit card number using a loop and the variable 'digit'.
    // Keep track of the amount and position of each digit using variable 'count'.

    while (card_num >= 0)
    {
        digit = card_num % 10 ;
        count++ ;
        temp_num = (card_num - digit) / 10 ;
        break ;


     // Apply Luhn's algorithm using two different 'for' loops depending on the position of each digit.

        for (count = 0 ; count % 2 == 0 ; count++)
        {
            sum_a = sum_a + ((card_num % 10) * 2) ;

            while ((card_num % 10) * 2 >= 10)
            {
                sum_a = (sum_a % 10) + 1 ;
            } 
        }

        for (count = 0 ; count % 2 != 0 ; count++)
        {
            sum_b = sum_b + digit ;
        }

        return sum_a ;
        return sum_b ;
        return count ;
    }

    // Checking the validity of the number according to Luhn's algorithm

    int total_sum = sum_a + sum_b ;

    if (total_sum % 10 != 0)
    {
       printf("This is an invalid number.\n") ; 
    }

// If the number entered doesn't have the right amount of digits according
// to variable 'count', declare the number as invalid.

    if (count != 13 || count != 15 || count != 16)
    {
        printf("This is an invalid number.\n") ;
    }

// Reset value of variable 'temp_num' and apply calculations that will isolate the first two digits. 
// Store the results in a variable 'company_id'.

    temp_num = card_num ;
    int company_id ;

    while (temp_num > 100)
    {
        temp_num = card_num - (card_num % 10) ;
        company_id = temp_num / 10 ;
    }

    return company_id ;

// Print the type of credit card depending on the company ID and amount of digits.

    if (company_id > 50 && company_id < 56 && count == 16)
    {
        printf("MASTERCARD\n") ;
    }
    else if ((company_id == 4) && (count == 13 || count == 16))
    {
        printf("VISA\n") ;
    }
    else if ((company_id == 34 || company_id == 37) && (count == 15)) 
    {
        printf("AMEX\n") ;
    }
    else
    {
        printf("This is an invalid number.\n") ;
    }

    return 0 ;

}

Answer

cdlane picture cdlane · Oct 12, 2016

Your answer is a out of order pastiche with sections that don't follow logically from the previous.

Specific issues:

This logic:

if (count != 13 || count != 15 || count != 16)

invalidates every card, the ors (||) should be ands (&&) for this to work.

This loop makes no sense:

while (card_num >= 0)
{
    digit = card_num % 10 ;
    count++ ;
    temp_num = (card_num - digit) / 10 ;
    break ;
    ...
}

The break is unconditional so it exits the loop and ignores the next twenty lines.

You appear to have spliced in subroutines from elsewhere as you call return five times, only the last of which is valid:

return sum_a ;
return sum_b ;
return count ;
return company_id ;
return 0 ;

In several places you use card_num when you should be using temp_num.

You fail to exit the program once you know the card is invalid -- instead you just keep on testing. You fail to acknowledge when a card is valid.

You count the number of digits in the card number but wait until after you run other checks before testing if that digit count was valid or not.

What follows is my rework of your code to address the above and some style issues:

#include <stdio.h>
#include <cs50.h>
#include <math.h>

int main(void)
{
    printf("Please give me your credit card number: ") ;

    long long card_num = 0LL;

    while (card_num < 1LL || card_num > 9999999999999999LL)
    {
        card_num = GetLongLong();
    }

    // Make a copy of the card number to be used and modified throughout the process.

    long long temp_num = card_num;

    // Isolate every digit from the credit card number using a loop and the variable 'digit'.
    // Keep track of the amount and position of each digit using variable 'count'.

    int count = 0;

    while (temp_num > 0LL)
    {
        temp_num = temp_num / 10LL;
        count++;
    }

    // If the number entered doesn't have the right amount of digits according
    // to variable 'count', declare the number as invalid.

    if (count != 13 && count != 15 && count != 16)
    {
        printf("This is an invalid number (# of digits).\n");
        return 1;
    }

    // Reset value of variable 'temp_num' and apply calculations that will isolate the first two digits. 
    // Store the results in a variable 'company_id'.

    temp_num = card_num;

    while (temp_num > 100LL)
    {
        temp_num = temp_num / 10LL;
    }

    int company_id = temp_num;

    // Print the type of credit card depending on the company ID and amount of digits.

    if (company_id > 50 && company_id < 56 && count == 16)
    {
        printf("MASTERCARD\n") ;
    }
    else if ((company_id == 34 || company_id == 37) && (count == 15)) 
    {
        printf("AMEX\n") ;
    }
    else if ((company_id / 10 == 4) && (count == 13 || count == 16 || count == 19))
    {
        printf("VISA\n") ;
    }
    else
    {
        printf("This card was issued by an unknown company.\n");
    }

    // Apply Luhn's algorithm.

    int sum = 0;

    temp_num = card_num;

    for (int i = 1; i <= count; i++)
    {
        int digit = temp_num % 10LL;

        if (i % 2 == 0)
        {
            digit *= 2;

            if (digit > 9)
            {
                digit -= 9;
            }
        }

        sum += digit;

        temp_num /= 10LL;
    }

    // Checking the validity of the number according to Luhn's algorithm

    if (sum % 10 != 0)
    {
        printf("This is an invalid number (Luhn's algorithm).\n");
        return 1; 
    }

    printf("This is a valid number.\n");

    return 0;
} 

This is not a finished program -- there's error checking and other details needed. Rather than summing the digits when a doubled card number is greater than 9, I used the simpler approach of subtracting 9.