Я покажу вам в вашей программе (со встроенными многострочными комментариями) все объектные вещи, которые я видел простым осмотром (мне пришлось немного его изменить, поскольку вы не предоставляете реализацию get_long(char *prompt)
function, и некоторые другие файлы, которые вы также не предоставляете. Позже я дам вам лучшее решение, в котором нет проблемы целочисленного предела, поскольку он использует строки для вычисления контрольной суммы. В конце есть ссылка на репозиторий github, в котором рассматриваются все версии решения (включая DFA --- Deterministi c Finite Automaton - вероятно, самое быстрое решение проблемы)
/* 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");
}
}
}
Нет необходимости преобразовывать цепочка цифр в число ... это сделает вашу обработку более сложной, и вам нужно будет перейти к long long
числам, чтобы использовать ее на самых длинных номерах карт.
Я разработал эту процедуру:
#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;
}
, который использует строку цифр и обрабатывает ее справа налево (начиная с конца строки) часть этого кода, опубликованная в github и которую вы можете скачать полную программу с здесь . Там вы найдете опубликованную здесь версию, если вы извлечете версию, помеченную как SO_60424279
, и в ветке master
вы получите реализацию DFA, управляемую таблицей, которая должна работать быстрее, чем эта.
Для компиляции просто выполните
make
в каталоге, в который вы извлекли источник.