ccs50luhn

CS50 Luhn's Algorithm in C


I'm new to C and I'm doing CS50. I can't get my code to work. I'm almost positive the for statement is correct. The sum given by the for statement is correct.

I think it's something to do with the (floor(ccNumber / pow(10,13) == 34) in the if statements.

Explanation of Luhn's Algorithm: https://cs50.harvard.edu/x/2020/psets/1/credit/

Sample credit card numbers for testing: https://www.freeformatter.com/credit-card-number-generator-validator.html

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

void credit(long ccNumber);

int main(void)
{
    long ccNumber = 0;
    credit(ccNumber);
}

void credit(long ccNumber)
{

    do

    {
        ccNumber = get_long("Enter a credit card number: ");
    }

    while (ccNumber < 0);

    {
        int sum = 0;
        long ccNumberFormat = ccNumber;
        int nDigits = floor(log10(ccNumberFormat)) + 1;
        int parity = nDigits % 2; // if parity % 2 == 0 then even number of digits, otherwise odd number of digits


        for (int i = nDigits; i >= 0; i--) {
            int digit = ccNumberFormat % 10;

            if (i % 2 != parity) { // even number credit card digits will be multiplied by 2 every even number (starting from 0)
                digit = digit * 2;
                // printf("%d\n", sum);
            }
            if (digit > 9) {
                digit = digit - 9;
            }

            sum = sum + digit;
            ccNumberFormat /= 10;
            printf("%d\n", sum);
        }

        if (sum % 10 == 0 && nDigits == 15 && (floor(ccNumber / pow(10,13) == 34) || (floor(ccNumber / pow(10,13) == 37))))
        {
            printf("%s\n", "AMEX");
        }
        else if (sum % 10 == 0 && (nDigits == 13 || nDigits == 16) && (floor(ccNumber / pow(10,12) == 4) || floor(ccNumber / pow(10,15) == 4)))
        {
            printf("%s\n", "VISA");
        }
        else if (sum % 10 == 0 && nDigits == 16 && ((floor(ccNumber / pow(10,14) >= 51) || (floor(ccNumber / pow(10,14) <= 55)))))
        {
            printf("%s\n", "MASTERCARD");
        }
    }
}

Pseudocode I more or less followed on Wikipedia::

function checkLuhn(string purportedCC) {
    int sum := integer(purportedCC[length(purportedCC)-1])
    int nDigits := length(purportedCC)
    int parity := nDigits modulus 2
    for i from 0 to nDigits - 2 {
        int digit := integer(purportedCC[i])
        if i modulus 2 = parity
            digit := digit × 2
        if digit > 9
            digit := digit - 9
        sum := sum + digit
    }
    return (sum modulus 10) = 0
}

Solution

  • A credit card number is a big number, are you sure long is enough and you do not need long long

    Out of that in your expressions like floor(ccNumber / pow(10,13) == 34) the ')' is wrongly placed and you wanted floor(ccNumber / pow(10,13)) == 34 and of course the same for others :

        if (sum % 10 == 0 && nDigits == 15 && ((floor(ccNumber / pow(10,13)) == 34) || (floor(ccNumber / pow(10,13)) == 37)))
        {
            printf("%s\n", "AMEX");
        }
        else if (sum % 10 == 0 && (nDigits == 13 || nDigits == 16) &&((floor(ccNumber / pow(10,12)) == 4) || (floor(ccNumber / pow(10,15)) == 4)))
        {
            printf("%s\n", "VISA");
        }
        else if (sum % 10 == 0 && nDigits == 16 && ((floor(ccNumber / pow(10,14)) >= 51) || (floor(ccNumber / pow(10,14)) <= 55)))
        {
            printf("%s\n", "MASTERCARD");
        }
    

    but in

        else if (sum % 10 == 0 && (nDigits == 13 || nDigits == 16) &&((floor(ccNumber / pow(10,12)) == 4) || (floor(ccNumber / pow(10,15)) == 4)))
    

    you take the risk to have a bad result, you need to check (floor(ccNumber / pow(10,12)) == 4) only if nDigits == 13 and (floor(ccNumber / pow(10,15)) == 4) only if nDigits == 16 and you can simplify to have :

        else if (sum % 10 == 0 && (nDigits == 13 || nDigits == 16) &&(floor(ccNumber / pow(10,nDigits-1)) == 4))
    

    So finally :

    #include <stdio.h>
    #include <math.h>
    
    void credit(long long ccNumber);
    
    int main(void)
    {
      long long cc[] = { 4532057997187363ll, // visa
                         4485661945778178ll, // visa
                         2720995573736457ll, // MasterCard
                         2720998284576493ll, // MasterCard
                         375137447049450ll, // amex
                         378572901284556ll, // amex
      };
    
     for (int i = 0; i != sizeof(cc)/sizeof(*cc); ++i)
        credit(cc[i]);
      
      return 0;
    }
    
    void credit(long long ccNumber)
    {
      int sum = 0;
      long long ccNumberFormat = ccNumber;
      int nDigits = floor(log10(ccNumberFormat)) + 1;
      int parity = nDigits % 2; // if parity % 2 == 0 then even number of digits, otherwise odd number of digits
      
      
      for (int i = nDigits; i >= 0; i--) {
        int digit = ccNumberFormat % 10;
        
        if (i % 2 != parity) { // even number credit card digits will be multiplied by 2 every even number (starting from 0)
          digit = digit * 2;
          // printf("%d\n", sum);
        }
        if (digit > 9) {
          digit = digit - 9;
        }
        
        sum = sum + digit;
        ccNumberFormat /= 10;
        //printf("%d\n", sum);
      }
      
      if (sum % 10 == 0 && nDigits == 15 && ((floor(ccNumber / pow(10,13)) == 34) || (floor(ccNumber / pow(10,13)) == 37))) {
          printf("%lld %s\n", ccNumber, "AMEX");
        }
      else if (sum % 10 == 0 && (nDigits == 13 || nDigits == 16) &&(floor(ccNumber / pow(10,nDigits-1)) == 4)) {
        printf("%lld %s\n", ccNumber, "VISA");
      }
      else if (sum % 10 == 0 && nDigits == 16 && ((floor(ccNumber / pow(10,14)) >= 51) || (floor(ccNumber / pow(10,14)) <= 55))) {
        printf("%lld %s\n", ccNumber, "MASTERCARD");
      }
    
    }
    

    Compilation and execution:

    pi@raspberrypi:/tmp $ gcc -Wall f.c -lm
    pi@raspberrypi:/tmp $ ./a.out
    4532057997187363 VISA
    4485661945778178 VISA
    2720995573736457 MASTERCARD
    2720998284576493 MASTERCARD
    375137447049450 AMEX
    378572901284556 AMEX
    pi@raspberrypi:/tmp $ 
    

    note also to do floating point computation is a risk, you can do all only using long long