C语言实现Luhn算法校验异常排查:部分VISA卡识别失效
信用卡校验代码问题排查:合法VISA卡号被误判为无效
我用C语言实现了Luhn算法及Amex、Mastercard、VISA的卡类型校验逻辑,卡类型校验功能正常,但Luhn算法的check_sum函数存在问题。check50测试显示,合法VISA卡号4111111111111111、4222222222222被判定为INVALID,期望输出为VISA。以下是完整代码及测试结果:
#include <cs50.h> #include <math.h> #include <stdio.h> long get_card_no(void); int digit_count(long card_no); int check_amex(long card_no, int noDigits); int check_mastercard(long card_no, int noDigits); int check_visa(long card_no, int noDigits); int check_sum(long card_no, int noDigits); int main(void) { long card_no = get_card_no(); int noDigits = digit_count(card_no); int amex = check_amex(card_no, noDigits); int mastercard = check_mastercard(card_no, noDigits); int visa = check_visa(card_no, noDigits); int checkSum = check_sum(card_no, noDigits); if (checkSum == 0) { if (amex) { printf("AMEX\n"); } else if (mastercard) { printf("MASTERCARD\n"); } else if (visa) { printf("VISA\n"); } else { printf("INVALID\n"); } } else { printf("INVALID\n"); } } long get_card_no(void) { long n; do { n = get_long("Number: "); } while(n < 0); return n; } int digit_count(long card_no) { int count = 0; while(card_no > 1) { card_no = card_no / 10; count++; } return count; } int check_sum(long card_no, int noDigits) { int digit1; int digit2; int sum1 = 0; int sum2 = 0; int sum; for (int i = 1; i < noDigits; i += 2) { digit1 = (int)(card_no / pow(10, i)) % 10; digit1 *= 2; if (digit1 > 9) { digit1 = digit1 / 10 + digit1 % 10; } sum1 += digit1; } for (int j = 0; j < noDigits; j += 2) { digit2 = (int)(card_no / pow(10, j)) % 10; sum2 += digit2; } sum = sum1 + sum2; return sum % 10; } int check_amex(long card_no, int noDigits) { int first_digit = card_no / pow(10, (noDigits - 1)); int second_digit = (int)(card_no / pow(10, (noDigits - 2))) % 10; if (noDigits == 15 && first_digit == 3 && (second_digit == 4 || second_digit == 7)) { return 1; } else { return 0; } } int check_mastercard(long card_no, int noDigits) { int first_digit = card_no / pow(10, (noDigits - 1)); int second_digit = (int)(card_no / pow(10, (noDigits - 2))) % 10; if (noDigits == 16 && first_digit == 5 && (second_digit == 1 || second_digit == 2 || second_digit == 3 || second_digit == 4 || second_digit == 5)) { return 1; } else { return 0; } } int check_visa(long card_no, int noDigits) { int first_digit = card_no / pow(10, (noDigits - 1)); if ((noDigits == 16 || noDigits == 13) && first_digit == 4) { return 1; } else { return 0; } }
check50 v3.3.8测试结果:
:) credit.c exists :) credit.c compiles :) identifies 378282246310005 as AMEX :) identifies 371449635398431 as AMEX :) identifies 5555555555554444 as MASTERCARD :) identifies 5105105105105100 as MASTERCARD :( identifies 4111111111111111 as VISA expected "VISA\n", not "INVALID\n" :) identifies 4012888888881881 as VISA :( identifies 4222222222222 as VISA expected "VISA\n", not "INVALID\n" :) identifies 1234567890 as INVALID :) identifies 369421438430814 as INVALID :) identifies 4062901840 as INVALID :) identifies 5673598276138003 as INVALID :) identifies 4111111111111113 as INVALID :) identifies 4222222222223 as INVALID
问题根源及修复方案
1. 数字计数函数digit_count循环条件错误
当前digit_count的循环条件为while(card_no > 1),会导致位数计算少一位:
- 13位卡号
4222222222222最后会停在card_no=1,返回12位而非正确的13位 - 16位卡号
4111111111111111同理少算一位
修复:将循环条件改为while(card_no > 0),确保所有数字都被计数:
int digit_count(long card_no) { int count = 0; while(card_no > 0) { card_no = card_no / 10; count++; } return count; }
2. pow函数的精度误差问题
pow是浮点函数,计算pow(10, i)时可能出现精度偏移(比如pow(10,15)实际值可能略小于1e15),强制转换为整数后会导致取数字时出错。
修复:用整数运算替代pow,或直接逐位处理数字避免幂运算。
修复后的check_sum函数(推荐逐位处理)
int check_sum(long card_no) { int sum1 = 0; int sum2 = 0; int count = 0; long temp = card_no; while (temp > 0) { int digit = temp % 10; if (count % 2 == 1) // 从右数第2、4、6...位 { digit *= 2; sum1 += (digit / 10) + (digit % 10); } else // 从右数第1、3、5...位 { sum2 += digit; } temp /= 10; count++; } return (sum1 + sum2) % 10; }
注意:修改后main函数调用check_sum时只需传入card_no,同时更新函数声明。
修复卡类型校验函数中的pow问题
以check_visa为例,用整数循环构建除数:
int check_visa(long card_no, int noDigits) { if (!(noDigits == 13 || noDigits == 16)) { return 0; } long divisor = 1; for (int i = 0; i < noDigits - 1; i++) { divisor *= 10; } int first_digit = card_no / divisor; return (first_digit == 4) ? 1 : 0; }
同理修改check_amex和check_mastercard函数中的幂运算部分。
3. 修复后的完整代码
#include <cs50.h> #include <stdio.h> long get_card_no(void); int digit_count(long card_no); int check_amex(long card_no, int noDigits); int check_mastercard(long card_no, int noDigits); int check_visa(long card_no, int noDigits); int check_sum(long card_no); int main(void) { long card_no = get_card_no(); int noDigits = digit_count(card_no); int amex = check_amex(card_no, noDigits); int mastercard = check_mastercard(card_no, noDigits); int visa = check_visa(card_no, noDigits); int checkSum = check_sum(card_no); if (checkSum == 0) { if (amex) { printf("AMEX\n"); } else if (mastercard) { printf("MASTERCARD\n"); } else if (visa) { printf("VISA\n"); } else { printf("INVALID\n"); } } else { printf("INVALID\n"); } } long get_card_no(void) { long n; do { n = get_long("Number: "); } while(n < 0); return n; } int digit_count(long card_no) { int count = 0; while(card_no > 0) { card_no = card_no / 10; count++; } return count; } int check_sum(long card_no) { int sum1 = 0; int sum2 = 0; int count = 0; long temp = card_no; while (temp > 0) { int digit = temp % 10; if (count % 2 == 1) { digit *= 2; sum1 += (digit / 10) + (digit % 10); } else { sum2 += digit; } temp /= 10; count++; } return (sum1 + sum2) % 10; } int check_amex(long card_no, int noDigits) { if (noDigits != 15) { return 0; } long divisor = 1; for (int i = 0; i < noDigits - 2; i++) { divisor *= 10; } int first_two = card_no / divisor; return (first_two == 34 || first_two == 37) ? 1 : 0; } int check_mastercard(long card_no, int noDigits) { if (noDigits != 16) { return 0; } long divisor = 1; for (int i = 0; i < noDigits - 2; i++) { divisor *= 10; } int first_two = card_no / divisor; return (first_two >= 51 && first_two <= 55) ? 1 : 0; } int check_visa(long card_no, int noDigits) { if (!(noDigits == 13 || noDigits == 16)) { return 0; } long divisor = 1; for (int i = 0; i < noDigits - 1; i++) { divisor *= 10; } int first_digit = card_no / divisor; return (first_digit == 4) ? 1 : 0; }
内容的提问来源于stack exchange,提问作者user22625969
相关产品推荐
相关产品推荐

