CS50 pset 1 credit (long divided by a number) - c

I am currently trying my best to solve the question. However, I have encountered the following problem:
After checking the sum, I want to verify the first 2 digits from the card no.
so I use the following method:
int main(void)
{
long long ccn;
do
{
ccn = get_long_long("Credit Card No.:\n");
}
while (ccn < 0);
int ccn_len;
long long count = ccn;
long long bccn = ccn;
for (ccn_len = 0; count != 0; ccn_len++, count /= 10);
int sum = 0; //checksum
for (int i = 0; i < ccn_len; ccn /= 10, i++)
{
if (i % 2 == 0)
{
sum += ccn % 10;
}
else
{
int digit = (ccn % 10) * 2;
sum += digit / 10 + digit % 10;
}
}
if (sum % 10 != 0)
{
printf("INVALID");
}
else
{
int a = bccn / 1e13;
if ((bccn / 1e13 == 34 || bccn / 1e13 == 37) && ccn_len == 15)
{
printf("AMERICAN EXPRESS");
}
else if (bccn / 1e12 == 4 && ccn_len == 13)
{
printf("VISA");
}
else if (ccn_len == 16)
{
if (bccn / 1e15 == 4)
{
printf("VISA");
}
if (bccn / 1e14 > 50 || bccn / 1e14 < 56)
{
printf("MASTERCARD");
}
}
else
{
printf("INVALID");
}
}
}
}
Let's say it is a valid AE card: 378282246310005 with a length of 15 digits
In the above code, I use ccn / 1e13 to get the first two digits to check whether it is 34 or 37.
However, after satisfying the checksum, the output still shows INVALID.
I try to use another method,
I set a variable a
and a = ccn / 1e13
and then I put a in the if-statement:
if ((a == 34 || a == 37) || ccn_len == 15)
everything works fine this time.
Can anyone tell me what is going wrong with my code? Or how do I write better?
Your replies are very much appreciated.

1e13 is a floating-point constant with type double. In bccn / 1e13, bccn is converted to double, and then floating-point division is performed, yielding a number such as 37.82822463100050214279690408147871494293212890625. Then bccn / 1e13 == 37 evaluates as false because 37.82822463100050214279690408147871494293212890625 is not equal to 37.
Rewrite your code to use only integer arithmetic (do not use floating-point constants like 1e13) or to treat credit card “numbers” as strings of digits rather than as integers.

Related

My attempt at Luhn's algorithm (for credit card validity) seems to recognise some cards but not others

I've constructed some code to simulate Luhn's algorithm for checking the validity of credit cards. It successfully recognises American Express cards (15 digit numbers beginning in 34 or 37), but when I try Mastercard (16 digits, beginning with 51, 52, 53, 54, or 55), it doesn't recognise them. Visa cards have 13 or 16 digits and start with the digit 4, my code seems to correctly identity the 16 digit cases but not the 13 digit ones. I've gone through my code and double checked my numerical ranges and I can't seem to diagnose exactly why some cards go through but others don't. Any help would be greatly appreciated.
Edit:
I've almost fixed the problem, I've modified it, and now all the card numbers check out, but now it's recognising an invalid number (4111111111111113) as Visa. Here's my updated code:
#include <math.h>
#include <cs50.h>
#include <stdio.h>
long long number;
int main(void)
{
long long i = 0;
long long b;
long long m = 10;
long long n = 1;
number = get_long_long("Number?\n");
do
{
long long a = number % m;
b = number - a;
long long c = b % (m * 10);
long long d = c / m;
long long e = d * 2;
if (e < 9)
{
i = i + e;
}
else
{
i = i + (e - 10 + 1);
}
{
m = m * 100;
}
}
while (b > 0);
do
{
long long a = number % n;
b = number - a;
long long c = b % (n * 10);
long long d = c / n;
long long e = d;
if (e < 9)
{
i = i + e;
}
else
{
i = i + (e - 10 + 1);
}
{
n = n * 100;
}
}
while (b > 0);
int f = i % 10;
if (((f == 0) && (number > 339999999999999) && (number < 350000000000000)) || ((number > 369999999999999) && (number < 380000000000000)))
{
printf("AMEX\n");
}
else
if ((f == 0) && (number > 5099999999999999) && (number < 5600000000000000))
{
printf("MASTERCARD\n");
}
else
if (((f == 0) && ((number > 3999999999999) && (number < 5000000000000))) || ((number > 3999999999999999) && (number < 5000000000000000)))
{
printf("VISA\n");
}
else
{
printf("INVALID\n");
}
}
I see that you are having trouble trying to verify the length of some cards. To fix this use a function that automatically checks the length of a long. You can use some logic with the function to properly identify the Visa lengths.
Here is the function to check the length of a long variable.
int get_long_len(long value)
{
int l = 1;
while (value > 9)
{
l++;
value /= 10;
}
return l;
}
It takes in the long value and returns the length.

CS50 Credit Card Validation

#include <stdio.h>
#include <cs50.h>
int main(void)
{
long cc = get_long("Credit Card: "); // gets input
long len = 0; //intialized length
long x = cc; // set 2nd variable = to cc to prevent manipulation of cc
while (x != 0) // length count loop while x is divisable loop will continue will be stored as len
{
x = x / 10;
len++;
}
if ((len != 16) && (len != 15) && (len != 13)) //Checking for length to see if number matchs possible postive outcomes
{
printf("INVALID\n");
return 0;
}
//pull 2nd to last and then every other digit
long cc_num1 = ((cc % 100) / 10);
long cc_num2 = ((cc % 10000) / 1000);
long cc_num3 = ((cc % 1000000) / (100000));
long cc_num4 = ((cc % 100000000) / (10000000));
long cc_num5 = ((cc % 10000000000) / (1000000000));
long cc_num6 = ((cc % 1000000000000) / (100000000000));
long cc_num7 = ((cc % 100000000000000) / (10000000000000));
long cc_num8 = ((cc % 10000000000000000) / (1000000000000000));
cc_num1 = (cc_num1 * 2); //Multiply digits pulled above by 2
cc_num2 = (cc_num2 * 2);
cc_num3 = (cc_num3 * 2);
cc_num4 = (cc_num4 * 2);
cc_num5 = (cc_num5 * 2);
cc_num6 = (cc_num6 * 2);
cc_num7 = (cc_num7 * 2);
cc_num8 = (cc_num8 * 2);
cc_num1 = ((cc_num1 / 10) + (cc_num1 % 10)); //split double digits and add to signles
cc_num2 = ((cc_num2 / 10) + (cc_num2 % 10));
cc_num3 = ((cc_num3 / 10) + (cc_num3 % 10));
cc_num4 = ((cc_num4 / 10) + (cc_num4 % 10));
cc_num5 = ((cc_num5 / 10) + (cc_num5 % 10));
cc_num6 = ((cc_num6 / 10) + (cc_num6 % 10));
cc_num7 = ((cc_num7 / 10) + (cc_num7 % 10));
cc_num8 = ((cc_num8 / 10) + (cc_num8 % 10));
long cc_sum = cc_num1 + cc_num2 + cc_num3 + cc_num4 + cc_num5 + cc_num6 + cc_num7 + cc_num8; // add sum of number above
long cc_num1x = ((cc % 10) / 1); //pulls last digit from card then everyother digit
long cc_num2x = ((cc % 1000) / 100);
long cc_num3x = ((cc % 100000) / 10000);
long cc_num4x = ((cc % 10000000) / 1000000);
long cc_num5x = ((cc % 1000000000) / 100000000);
long cc_num6x = ((cc % 100000000000) / 10000000000);
long cc_num7x = ((cc % 10000000000000) / 1000000000000);
long cc_num8x = ((cc % 1000000000000000) / 100000000000000);
long cc_sumx = cc_num1x + cc_num2x + cc_num3x + cc_num4x + cc_num5x + cc_num6x + cc_num7x +
cc_num8x; //adds last and everyother digit together
long sumofsums = cc_sum + cc_sumx; // adds sums of both sums created
if ((sumofsums % 10) != 0) // Luhn’s Algorithm results will close if not met
{
printf("INVALID\n");
return 0;
}
{
if (len == 15) // checks for AMEX by using length then first 2 digits
{
long ax = cc / 10000000000000;
if ((ax == 34 || ax == 37))
{
printf("AMEX\n");
}
else
{
printf("INVALID\n");
return 0;
}
}
}
long mc = cc / 100000000000000;
long v = cc / 1000000000000000;
long v2 = cc / 1000000000000;
if (len == 16) // Checks for MC and Via (16 digits) by length then first 2 digits MC or 1 visa
{
if ((mc == 51 || mc == 52 || mc == 53 || mc == 54 || mc == 55))
{
printf("MASTERCARD\n");
}
else if (v == 4)
{
printf("VISA\n");
}
else
{
printf("INVALID\n");
return 0;
}
}
if (len == 13) //Checks 2nd Visa length 13 digits then 1st digit
{
if (v2 == 4)
{
printf("VISA\n");
}
else
{
printf("INVALID\n");
return 0;
}
}
}
There has to be a better way then the way I am planning to do this. The Length count loop is fine until 10 digits but then pulls random numbers.
The every other digit formula seems like it can be done through recursion but I am blanking on that. Since the number is limited to 16 at most the formula I am using seems to work.
Determine if card is 15 || 16 || 13 digits if not mark In valid in IF Else loop
Use CC check sum formula If else loop (In valid if it doesn't meet Criteria)
Look at 2 Starting numbers to determine AX, MC or Visa
#include <stdio.h>
#include <cs50.h>
#include <string.h>
int main(void)
{
long cc = get_long("Credit Card: " ); // gets input
int len = 0; //intialized length
int x = cc; // set 2nd variable = to cc to prevent manipulation of cc
while(x != 0) // length count loop while x is divisable loop will continue will be stored as len
{
x = x/10;
len++;
}
printf("%i\n", len); // REMOVE !!!!!!!!!!! BUG TEST
//pull 2nd to last and then every other digit
int cc_num1 = ((cc % 100)/10);
int cc_num2 = ((cc % 10000)/1000);
int cc_num3 = ((cc % 1000000)/(100000));
int cc_num4 = ((cc % 100000000)/(10000000));
int cc_num5 = ((cc % 10000000000)/(1000000000));
int cc_num6 = ((cc % 1000000000000)/(100000000000));
int cc_num7 = ((cc % 100000000000000)/(10000000000000));
int cc_num8 = ((cc % 10000000000000000)/(1000000000000000));
printf("%i %i %i %i %i %i %i %i", cc_num1, cc_num2, cc_num3, cc_num4 , cc_num5, cc_num6 , cc_num7 , cc_num8 );
}
Let's acknowledge the elephant in the room first.
long cc = get_long("Credit Card: " );
...
int x = cc;
The C standard specifies long to be at least 32 bits, whereas int must be at least 16 bits. The actual values are dependent on your system and your library implementation of course. But more often than not, long will be capable of storing more bits than an int. As is the case here. This means "numbers with more than 10 digits", essentially numbers that are too large to be stored into an int, will cause undefined behavior. To know exactly which number is the upper limit for int in your system/environment, you may print the value of INT_MAX, defined in limits.h.
The solution is, of course, to store the long variable in another long variable, not an int. Or, simply pass the value to a function that does the necessary work. Putting everything in main isn't being very organized now is it.
How about we make a function that basically prints all the details about a card given the card's number?
The signature will look like-
void print_card_details(long num)
Now we need a function to put the card through luhn's algorithm. We can also make a function for that-
int is_valid(long num)
{
int curr_digit, add_digit, prod_sum = 0, sum = 0;
for (int digit_count = 0; num != 0; num /= 10, digit_count++)
{
// Strip each digit from number, starting from the end
curr_digit = num % 10;
if (digit_count % 2 != 0)
{
// Every 2nd digit from the right goes through this
// The current digit gets doubled
// The digits of that result are added to the sum
add_digit = curr_digit * 2;
prod_sum += add_digit % 10 + add_digit / 10;
}
else
{
// The remaining digits go through this
// They are all summed up
sum += curr_digit;
}
}
if ((prod_sum + sum) % 10 != 0)
{
// If the sum of prod_sum + sum doesn't end in 0
// It is invalid
return 0;
}
else
{
// The card is valid
return 1;
}
}
The conventional way to iterate through the digits of a number is not to bruteforcefully divide arbitrary powers of 10 manually, but to iterate through it and divide and modulus by 10. For example, this snippet-
while (x != 0)
{
printf("Current digit: %d\n", x % 10);
x /= 10;
}
Will print all digits of the number stored in x. This is essentially what we've used in the luhn's algorithm loop. Except we also keep a count of the total digits, because we only want every second digit starting from the end. How do we know the current digit qualifies this criteria? We check if the current digit_count is even (by dividing by 2 and checking the leftover is 0).
The formula that follows-
add_digit = curr_digit * 2;
prod_sum += add_digit % 10 + add_digit / 10;
is basically the implementation of this-
Multiply every other digit by 2, starting with the number’s second-to-last digit, and then add those products’ digits together.
Make sure only the digits of the resulting add_digit is added. So if add_digit ended up being 12. We need to add 1 + 2. That's exactly what add_digit % 10 + add_digit / 10 does. 12 % 10 is, of course, 2. And 12 / 10 is 1.
This function returns 1 if the card is valid, 0 if it's not. You can fit this up in your main function and check the return value to know whether the card is valid.
If it is valid, move on to the next step of checking the number of digits the card has, as well as what it begins with.
We can make a loop to count the number of digits, as well as store the very first and second digit of the number.
int len = 0;
int curr_digit = 0, prev_digit = 0;
while(num != 0)
{
prev_digit = curr_digit;
curr_digit = num % 10;
num /= 10;
len++;
}
This will give you the length of the card number. Notice, in the last iteration, the value of prev_digit is the second digit and the curr_digit is the first. So curr_digit * 10 + prev_digit will yield the first 2 numbers (together) that the credit card number begins with.
Finally, you just need a bunch of simple if/else clauses to verify which card it is. You're only asked to check for a very small subset as well. So here it is-
// Construct the 2 digit number that this card num begins with
int begins_with = curr_digit * 10 + prev_digit;
if (len == 13 && begins_with / 10 == 4)
{
// We know only VISA uses 13 digits
// And it begins with 4 (second digit does not matter)
printf("VISA\n");
}
else if (len == 15 && begins_with == 34 ||)
{
// We know only AMEX uses 15 digits
printf("AMEX\n");
}
else if (len == 16)
{
// Both VISA and MASTERCARD use 16 digits
if (curr_digit == 4)
{
// But VISA card number begins with 4
printf("VISA\n");
}
else if (curr_digit == 5)
{
// MASTERCARD number begins with 5
printf("MASTERCARD\n");
}
else
{
// Out of context for this problem
printf("INVALID\n");
}
}
else
{
// Out of context for this problem
printf("INVALID\n");
}
Put that all together, and you should hopefully get
void print_card_details(long num)
{
if (!is_valid(num))
{
// Card did not pass luhn's algo
printf("INVALID\n");
return;
}
int len = 0;
int curr_digit = 0, prev_digit = 0;
while(num != 0)
{
prev_digit = curr_digit;
curr_digit = num % 10;
num /= 10;
len++;
}
// Construct the 2 digit number that this card num begins with
int begins_with = curr_digit * 10 + prev_digit;
if (len == 13 && curr_digit == 4)
{
// We know only VISA uses 13 digits
// And it begins with 4 (second digit does not matter)
printf("VISA\n");
}
else if (len == 15 && (begins_with == 34 || begins_with == 37))
{
// We know only AMEX uses 15 digits
printf("AMEX\n");
}
else if (len == 16)
{
// Both VISA and MASTERCARD use 16 digits
if (curr_digit == 4)
{
// But VISA card number begins with 4
printf("VISA\n");
}
else if (begins_with >= 51 && begins_with <= 55)
{
// MASTERCARD number begins with 51, 52, 53, 54, or 55
printf("MASTERCARD\n");
}
else
{
// Out of context for this problem
printf("INVALID\n");
}
}
else
{
// Out of context for this problem
printf("INVALID\n");
}
return;
}

Detect valid credit card number algorithm

I just enrolled in the online CS50 course and doing the pset1 about detecting valid credit card number. However, my algorithm does not work well as I expected. I used debugging tool to see step by step result, as long as variable i run from 1 to 9 it works perfectly, all values are added to the sum correctly. But when it comes to i = 10 and so on, the numNotSquared got assigned -8 and numSquared got assigned -16 and it keeps like that until the end of the number. Please help me with that, thank you.
// Headers and libraries
#include <stdio.h>
#include <cs50.h>
#include <math.h>
int main(void)
{
// Prompt user for card number
long cardNumber = get_long("Enter card number: ");
// Get the length of input
int length = floor(log10(cardNumber)) + 1;
// Range validation
if (length < 13 || length > 16)
{
printf("INVALID\n");
}
int numSquared = 0;
int numNotSquared = 0;
int sum = 0;
// Algorithm to detect valid card
// Based on Luhn's algorithm (https://lab.cs50.io/cs50/labs/2020/x/credit/)
for (int i = 1; i <= length; i++)
{
// If digit is on odd position then mutiply by two
if (i % 2 != 0)
{
{
numSquared = ((int)(cardNumber / pow(10, length - i)) % 10) * 2;
}
// If the total is >= 10, then sum the products' digit
if (numSquared >= 10)
{
sum += ((numSquared % 10) + 1);
}
else
{
sum += numSquared;
}
}
// If digit is on even position then add to the sum
else
{
numNotSquared = (int)(cardNumber / pow(10, length - i)) % 10;
sum += numNotSquared;
}
}
// Find remainder of (total / 10)
if (sum % 10 == 0)
{
if (floor(cardNumber / pow(10, length - 1)) == 4)
{
printf("VISA\n");
}
else
{
int firstTwoDigits = floor(cardNumber / pow(10, length - 2));
if (firstTwoDigits == 34 || firstTwoDigits == 37)
{
printf("AMEX\n");
}
else if (firstTwoDigits == 51 || firstTwoDigits == 52 || firstTwoDigits == 53 || firstTwoDigits == 54 || firstTwoDigits == 55)
{
printf("MASTERCARD\n");
}
}
}
else
{
printf("INVALID\n");
}
}

CS50 Problem Set 1 (Credit) 2020 help needed

I am trying to prompt the user for a credit card number and determine whether it is a real credit card number or not, and if so what type of credit card number.
I thought I'd finally got it however when doing check50 the following two inputs produce no output:
1234567890
4111111111111113
They should be giving INVALID but I can't figure out why they aren't giving any output.
This is my code:
#include <stdio.h>
#include <cs50.h>
#include <math.h>
int main(void)
{
long Card_Number;
int Digit_Number = 0, Current_Digit = 0, Even_x2_Product = 0, Even_Digits = 0, Odd_Digits = 0,
Total_Digit_Sum = 0;
bool is_even = false;
// Prompt User for Credit Card Number
do
{
Card_Number = get_long("Card Number: ");
}
while (Card_Number < 0);
// Check First Digits of Number
int Digits_MstrCrd = Card_Number / pow(10, 14);
int Digits_Visa_16 = Card_Number / pow(10, 15);
int Digits_AmEx = Card_Number / pow(10, 13);
int Digits_Visa_13 = Card_Number / (pow(10, 12));
// Loop to determine identity of each digit
while (Card_Number != 0)
{
// Get Last Digit of Number
Current_Digit = (Card_Number % 10);
// Increase Digit Number by 1
Digit_Number += 1;
// Check if Current Digit is at Odd or Even Position in Card Number
if (is_even == true)
{
// Multiply Digit by 2
Even_x2_Product = Current_Digit * 2;
// Add Digits of Multiplication Product
while (Even_x2_Product != 0)
{
Even_Digits += Even_x2_Product % 10;
Even_x2_Product /= 10;
}
// Tell Program Next Digit is Odd
is_even = false;
}
else
{
// Add Odd Digits
Odd_Digits += Current_Digit;
// Tell Program Next Number is Even
is_even = true;
}
// Remove Last Digit and Repeat
Card_Number /= 10;
}
// Add Odd and Even Digits Together
Total_Digit_Sum = Even_Digits + Odd_Digits;
// Loop to Check if Card Number is Valid
if (Total_Digit_Sum % 10 == 0)
{
// Check Mastercard
if (Digit_Number == 16)
{
if (Digits_MstrCrd <= 55 && Digits_MstrCrd >= 51)
{
printf("MASTERCARD\n");
}
// Check Visa 16
else if (Digits_Visa_16 == 4)
{
printf("VISA\n");
}
else
{
printf("INVALID\n");
}
}
// Check American Express
else if (Digit_Number == 15)
{
if (Digits_AmEx == 34 || Digits_AmEx == 37)
{
printf("AMEX\n");
}
else
{
printf("INVALID\n");
}
}
// Check Visa 13
else if (Digit_Number == 13)
{
if (Digits_Visa_13 == 4)
{
printf("VISA\n");
}
else
{
printf("INVALID\n");
}
}
else
{
printf("INVALID\n");
}
}
}
What does the program print if Total_Digit_Sum % 10 is not equal to 0? It has no else; there are no commands after the closing } of the block.
I'll show you in your program (with embedded multiline comments) all the objecttionable things I've seen by simple inspection (I've had to modify it a little, as you don't provide an implementation of get_long(char *prompt) function, and some other files you also don't provide. Later I give you a better solution, that doesn't have the problem of the integer limit, as it uses strings to calculate the checksum.
At the end there's a reference to a github repository where all versions of the solution are considered (including a DFA ---Deterministic Finite Automaton--- probably the fastest solution to the problem)
/* sorry, I need to comment this, as you have not provided this
* file. */
//#include <cs50.h>
/* you don't need math.h if you are using only integers */
//#include <math.h>
/* what is needed is stdbool.h, to use booleans in C */
#include <stdbool.h>
#include <stdio.h> /* and stdio, of course */
unsigned long long get_long(char *prmpt)
{
unsigned long long result;
fprintf(stderr, "%s> ", prmpt);
/* this loop is not protected against EOF, so you will have
* to interrupt the program if you reach the end of file
* here. */
while (scanf("%llu", &result) != 1)
fprintf(stderr, "?? >");
return result;
}
int main(void)
{
/* you need a 64bit number, so better use a long long here
* 32bit integers range only up to 4294967296, which is too
* short to use in your problem.
* on dividing your card number by 100000000000000 you'll
* allways get 0.
*/
long Card_Number;
int Digit_Number = 0, Current_Digit = 0, Even_x2_Product = 0, Even_Digits = 0, Odd_Digits = 0,
Total_Digit_Sum = 0;
bool is_even = false;
// Prompt User for Credit Card Number
do
{
Card_Number = get_long("Card Number: ");
}
while (Card_Number < 0);
// Check First Digits of Number
/* don't use pow(3) to produce a constant to divide
* in floating point by a power of ten. It allways
* produces inexact results, ad 1/10 cannot be represented
* as a finite number of digits in base 2. Just use
* 100000000000000LL, instead.
* In order to get the ttype of card, it is better to compare
* the number, as in
* // number is 15digits, at least
* if (Card_number >= 1000000000000000ULL) {
* Digit_number = 15;
* } else if (Card_number >= 10000000000000ULL) {
* Digit number = 14;
* } else if (Card_number >= 1000000000000ULL) {
* Digit_number = 13;
* ...
*/
int Digits_MstrCrd = Card_Number / pow(10, 14);
int Digits_Visa_16 = Card_Number / pow(10, 15);
int Digits_AmEx = Card_Number / pow(10, 13);
int Digits_Visa_13 = Card_Number / (pow(10, 12));
// Loop to determine identity of each digit
while (Card_Number != 0)
{
// Get Last Digit of Number
Current_Digit = (Card_Number % 10);
// Increase Digit Number by 1
/* why do you increment the digit by one, the digit value
* is just that, the remainder of the integer division.
*/
Digit_Number += 1;
// Check if Current Digit is at Odd or Even Position in Card Number
/* better use if (is_even) as is_even is already a
* boolean */
if (is_even == true)
{
// Multiply Digit by 2
Even_x2_Product = Current_Digit * 2;
// Add Digits of Multiplication Product
/* Even_x2_Product cannot be higher that 18,
* so why not just check if it is greater than 10
* and then subtract 10 and add 1 (or better,
* just subtract 9), as in:
if (Even_x2_Product >= 10)
Even_x2_product -= 9;
*/
while (Even_x2_Product != 0)
{
Even_Digits += Even_x2_Product % 10;
Even_x2_Product /= 10;
}
// Tell Program Next Digit is Odd
/* Shouldn't we add this result somewhere,
* mod 10 ??? Like in:
accumulated_checksum += Even_x2_Product;
Note: you do in the odd part.
*/
is_even = false;
}
else
{
/* I suggest you to add all digits together.
* As in:
accumulated_checksum += Current_digit;
*/
// Add Odd Digits
Odd_Digits += Current_Digit;
// Tell Program Next Number is Even
is_even = true;
}
/* if we have added two digits (the accumulated_checksum
* and the calculated one, no possibility of having more
* than 18 as the sum is possible, so check if the result
* is 10 or more, and subtract 10 to eliminate the carry.
if (accumulated_checksum >= 10)
accumulated_checksum -= 10;
*/
// Remove Last Digit and Repeat
Card_Number /= 10;
}
/* you can use only one sum. Both are digits... and if you
* have made the checks suggested above, it is already a number
* modulo 10. */
// Add Odd and Even Digits Together
/* this is not necessary */
Total_Digit_Sum = Even_Digits + Odd_Digits;
// Loop to Check if Card Number is Valid
/* you don't need to calculate the modulo 10 here, as you
* have eliminated all the higher digits in the last loop.
*/
if (Total_Digit_Sum % 10 == 0)
if (Total_Digit_Sum % 10 == 0)
{
// Check Mastercard
/* this is not the number of digits you have, this is the
* integer result of the division by a huge number...
* most of the times this will be zero, but it never be
* 16, with the numbers you are giving for the cards. */
if (Digit_Number == 16)
{
if (Digits_MstrCrd <= 55 && Digits_MstrCrd >= 51)
{
printf("MASTERCARD\n");
}
// Check Visa 16
else if (Digits_Visa_16 == 4)
{
printf("VISA\n");
}
else
{
printf("INVALID\n");
}
}
// Check American Express
/* also this is not true, by the same reason above. */
else if (Digit_Number == 15)
{
if (Digits_AmEx == 34 || Digits_AmEx == 37)
{
printf("AMEX\n");
}
else
{
printf("INVALID\n");
}
}
// Check Visa 13
/* same as above */
else if (Digit_Number == 13)
{
if (Digits_Visa_13 == 4)
{
printf("VISA\n");
}
else
{
printf("INVALID\n");
}
}
else
{
/* so you always end here */
printf("INVALID\n");
}
}
}
There's no need to convert the string of digits into a number... this will make your processing more complicated, and you will need to swith to long long numbers to use it on the longest card numbers.
I have developed this routine:
#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include "main.h"
#include "proc.h"
int process(const char *str)
{
int l = strlen(str);
const char *p = str + l;
int res = 0;
enum {
ODD_DIGIT,
EVEN_DIGIT,
} pos = ODD_DIGIT;
DEB("processing: [%s]\n", str);
while (--p >= str) {
if (!isdigit(*p)) {
WARN("%s\n", str);
WARN("%*s^: is not a digit\n", (int)(p-str), "");
return -1;
}
int dig = *p - '0';
switch (pos) {
case ODD_DIGIT: pos = EVEN_DIGIT;
DEB("Add dig(%d) to res(%d)\n", dig, res);
res += dig; break;
case EVEN_DIGIT: pos = ODD_DIGIT;
DEB("Add double(dig(%d)) to res(%d)\n", dig, res);
dig <<= 1;
if (dig >= 10)
dig -= 9;
res += dig; break;
}
if (res >= 10)
res -= 10;
DEB("res <= %d\n", res);
}
DEB("Returning => %d\n", res);
if ((flags & FLAG_QUIET) == 0) {
printf("%s: %d\n", str, res);
}
return res;
}
that uses a string of digits, and processes it from right to left (beginning on the end of the string) It is part of this code, published in github and that you can download the complete program from here. You'll find there the version published here, if you checkout the version tagged as SO_60424279, and in the branch master you'll get a table driven DFA implementation that should run faster than this one.
To compile, just execute
make
in the directory you extracted the source.
#include <stdio.h>
#include<cs50.h>
#include <math.h>
int main(void)
{
long x = get_long("enter the credit card number");
int digit = 0, sum = 0;
//digit is used for odd and even checker.
long y = x;
//checksum card digits
while (y != 0)
{
int sumeven = 0, sumodd = 0;
int rem = y % 10;
digit++;
if (digit % 2 == 0) //if digit is even
{
int multiply = rem * 2;
if (multiply == 0)
{
sumeven += multiply;
}
else
{
while (multiply != 0) //adding all digits after
{
sumeven += multiply % 10;
multiply /= 10; //minus last digit of multiply
}
}
}
else //if digit is odd
{
sumodd += rem;
}
y /= 10; //minus last digit from y
sum += sumeven + sumodd;
}
//check for valid credit card
if (digit != 13 && digit != 15 && digit != 16) //for first if
{
printf("INVALID\n");
}
else if (sum % 10 == 0)
{
if (digit == 16) //if digit is 16
{
if (x / 100000000000000 >= 51 && x / 100000000000000 <= 55)
{
printf("MASTERCARD\n");
}
else if (x / 1000000000000000 == 4)
{
printf("VISA\n");
}
else //if digit is not 16
{
printf("INVALID\n");
}
}
else if (digit == 15)
{
if (x / 10000000000000 == 34 || x / 10000000000000 == 37)
{
printf("AMEX\n");
}
else
{
printf("INVALID\n");
}
}
else if (digit == 13)
{
if (x / 1000000000000 == 4)
{
printf("VISA\n");
}
else
{
printf("INVALID\n");
}
}
}
else
{
printf("INVALID\n");
}
}`

'if' statement in C not executing even though conditions are met

I'm a first time programmer trying to complete a simple command line program as part of the first assignment for an online course I am taking, but I seem to have hit a roadblock that I can't figure out with GDB or my own research.
After hours of rewrites, and hours of debugging, I finally got the code below to compile. The program is supposed to take a credit card number as an input, and then check whether it's valid per the specifications of the assignment. I used a test number from here: PayPal Test Credit Cards
The odd thing is, when I enter an AMEX card number, it correctly produces the text "AMEX", but when I try a Visa or a Master Card, it prints "INVALID".
In GDB I broke at the Verify function and it seems to incorrectly skip these two if/else if statements without proceeding to the Checksum function even though conditions appear to be met.
if (firstDigit == 4 && totalDigits == (13 | 16) && Checksum(cardNumber, totalDigits) == 0) // checks for a valid Visa.
...
else if (firstDigit == 5 && secondDigit == (1 | 2 | 3 | 4 | 5) && totalDigits == 16 && Checksum(cardNumber, totalDigits) == 0) // checks for a valid Mastercard.
...
The AMEX line of code that correctly executes is:
else if (firstDigit == 3 && secondDigit == (4 | 7) && totalDigits == 15 && Checksum(cardNumber, totalDigits) == 0) // checks for a valid American Express.
The arguments for all three lines seem to be formatted exactly the same. That is far as I could get in GDB though. I would print totalDigits, firstDigit, and secondDigit in GDB right before stepping through the above two non-executing lines and everything looked correct. So I'm stumped, why is the AMEX line executing, but not the others?
Thanks in advance everyone. This is the first program after hello.c that I've tried to write, so I am open to absolutely any criticism or suggestions if it looks like I'm doing something weird/wrong.
Full code:
checker.c
#include <stdio.h>
#include <stdlib.h>
int MAX = 16;
int* DigitSort(unsigned long long x, int* array);
int Verify(int* array);
int main (void)
{
int* output = malloc (sizeof(int) * (MAX + 2)); // creates a blank array for the individual digits of the card number.
unsigned long long userInput = 0;
do
{
printf("Please enter a credit card number:\n");
scanf("%lld", &userInput);
}
while (userInput <= 0); // checks to make sure the user entered a number.
switch(Verify(DigitSort(userInput, output))) // sorts the user's input into individual digits and verifies the card type and validity.
{
case 1 :
printf("VISA\n");
break;
case 2 :
printf("MASTERCARD\n");
break;
case 3 :
printf("AMEX\n");
break;
case 0 :
printf("INVALID\n");
break;
default :
printf("INVALID\n");
}
free(output);
return 0;
}
int Verify(int* array) // verifies whether or not a card number is valid. Must pass the function a sorted array of individual digits.
{
int* cardNumber = array;
int firstDigit = cardNumber[0];
int secondDigit = cardNumber[1];
int totalDigits = 0;
int Checksum(int* cardNumber, int totalDigits);
int i = 0;
while (firstDigit >= 1 && cardNumber[i] >= 0) // this step counts the number of digits in the array.
{
totalDigits = totalDigits + 1;
i++;
}
if (firstDigit == 4 && totalDigits == (13 | 16) && Checksum(cardNumber, totalDigits) == 0) // checks for a valid Visa.
{
return 1;
}
else if (firstDigit == 5 && secondDigit == (1 | 2 | 3 | 4 | 5) && totalDigits == 16 && Checksum(cardNumber, totalDigits) == 0) // checks for a valid Mastercard.
{
return 2;
}
else if (firstDigit == 3 && secondDigit == (4 | 7) && totalDigits == 15 && Checksum(cardNumber, totalDigits) == 0) // checks for a valid American Express.
{
return 3;
}
else // if the card number doesn't match any of the above conditions or fails the checksum, an 'I' for Invalid is returned.
{
return 0;
}
}
int* DigitSort(unsigned long long x, int* array) // takes a long long as input and sorts it into individual digits
{
int* arrayReversed = malloc (sizeof(int) * (MAX + 2)); // creates a new array to hold the reversed order of digits.
int i = 0;
arrayReversed[0] = 0;
if (i < (MAX - 1) && x >= 10)
{
do
{
arrayReversed[i] = x % 10;
x = x / 10;
i++;
}
while (i < (MAX -1) && x >= 10);
}
if (i < MAX && x >= 1 && x <= 9)
{
arrayReversed[i] = (int) x;
x = (x - x);
}
if (x == 0)
{
int j = 0;
do
{
array[j] = arrayReversed[i]; // sorts the digits from the reversed array and places them into the sorted array.
j++;
i--;
}
while (j < MAX && i >= 0);
array[j] = -1;
}
free(arrayReversed);
return array;
}
int Checksum(int* cardNumber, int totalDigits)
{
int sum1 = 0;
int sum2 = 0;
int i = (totalDigits - 2);
int j = (totalDigits - 1);
while (i >= 0)
{
sum1 = ((cardNumber[i] * 2)%10) + ((cardNumber[i] * 2)/10) + sum1;
i -= 2;
}
while (j >= 0)
{
sum2 = (cardNumber[j] + sum2);
j -= 2;
}
if (((sum1 + sum2) % 10) == 0)
{
return 0;
}
else
{
return 1;
}
}
Your first problem is here:
if (firstDigit == 4 && totalDigits == (13 | 16) && ...
You need to write:
if (firstDigit == 4 && (totalDigits == 13 || totalDigits == 16) && ...
Your first check is looking for 0x1D == 29 as the number of digits (because, as paisanco points out in a comment, the | operator is the bitwise OR operator), and no credit card needs 29 digits (yet, and not for a long time to come). Note the extra parentheses for clarity and accuracy. Don't mess around risking removing them — the code won't work properly again. And in general, be explicit if your condition has both && and || operators and use parentheses to group terms explicitly.
You have similar problems elsewhere. As it happens, (4 | 7) is the same value as 7, so the condition works when the second digit is 7 (but not when it is 4). But it doesn't mean what you intended it to mean.
Computer languages don't work the same as human languages. Get used to writing out the condition somewhat more verbosely. Some other languages provide shorthands for these conditions; C is not such a language.

Resources