LinuxQuestions.org Basic C programming question
 Programming This forum is for all programming questions. The question does not have to be directly related to Linux and any language is fair game.

 12-15-2022, 03:42 PM #1 lxs602 Member   Registered: Oct 2018 Distribution: Ubuntu 24.04 Posts: 50 Rep: Basic C programming question Hi, I am taking the Harvard CS50 online course. The programme is to check credit card number validity. Why does the checkfunction() fail with a floating point error, when tested with a valid Mastercard number? I also tried using gdb, but it gave an error to do with get_long being not declared. Any other comments on how to improve the code very welcome. NB. I'm not sure if the maths works yet... Code: ```#include #include // x is the input long x; // y is the calculated checksum int y = 0; // i is an integer for calculations int i = 1; // Function to loop through the digits of the card number, beginning with the last digit. See HP Luhm algorithm for further details int checkfunction(void) { printf("made it here also\n"); // for (i = i + 0; i <= x; i *= 1e2) { // Divide the number by a multiple of 10 and take the remainder. Multiply the digit by 2 and add to the checksum. int z; z = x % 10; if (z <= 4) { y = z * 2 + y; } // If the product would be >10, add each digit of the product separately to the checksum else if (z >= 6) { y = ( z * 2 ) - 10 + 1 + y; } // Add the next digit along (from right to left) y = (x % (i * 10)) + y; } return y; } int main(void) { x = get_long("Number: "); // Exclude numbers shorter than 13 digits, longer than 16 digits, or that are 14 digits if (x < 1e12 || x > 1e16 - 1 || (x >= 1e13 && x <= 1e14)) { printf("INVALID\n"); } else { // For 15 digit cards if (x > 1e14 && x < 1e15 - 1) { // Check first two digits are 34 or 37, for Amex if ((x < 34e13 && x > 1e14 - 1) || (x > 37e13 && x < 38e13 - 1)) { // Loop through the digits of the card number, beginning with the last digit checkfunction(); // If checksum is divisible by 10, then credit card number is valid if ((y % 10) == 0) { printf("AMEX\n"); } else { printf("INVALID\n"); } } } // For 13 digit VISA cards, beginning with 4 else if (x > 4e12 && x < 5e12 - 1) { // Loop through the digits of the card number, beginning with the last digit checkfunction(); // If checksum is divisible by 10, then credit card number is valid if ((y % 10) == 0) { printf("VISA\n"); } else { printf("INVALID\n"); } } // For 16 digit VISA cards, also beginning with 4 else if (x > 4e15 && x < 5e15 - 1) { // For 16 digit cards, set i to 1 i = 1; // Loop through the digits of the card number, beginning with the last digit checkfunction(); // If checksum is divisible by 10, then credit card number is valid if ((y % 10) == 0) { printf("VISA\n"); } else { printf("INVALID\n"); } } // For Mastercards (16 digits), beginning with 51, 52, 53, 54 or 55 else if (x > 51e14 && x < 56e14 - 1) { printf("made it to this point\n"); // Loop through the digits of the card number, beginning with the last digit checkfunction(); printf("%i", y); // If checksum is divisible by 10, then credit card number is valid if ((y % 10) == 0) { printf("MASTERCARD\n"); } else { printf("INVALID\n"); } } else { printf("INVALID\n"); } } }``` Last edited by lxs602; 12-15-2022 at 04:05 PM.
 12-15-2022, 03:53 PM #2 teckk LQ Guru   Registered: Oct 2004 Distribution: Arch Posts: 5,190 Blog Entries: 6 Rep: Line 40 Code: `x = get_long("Number: ");`
 12-15-2022, 04:04 PM #3 lxs602 Member   Registered: Oct 2018 Distribution: Ubuntu 24.04 Posts: 50 Original Poster Rep: In the wrong place / incorrectly assigned?
 12-15-2022, 04:07 PM #4 teckk LQ Guru   Registered: Oct 2004 Distribution: Arch Posts: 5,190 Blog Entries: 6 Rep: I downloaded that header file from here https://raw.githubusercontent.com/cs...ain/src/cs50.h I included it with Code: `gcc test.c -o test -I/home/me/` And sure enough Code: ```gcc test.c -o test -I/home/me/ /usr/bin/ld: /tmp/ccJBimDQ.o: in function `main': aaa.c:(.text+0x12b): undefined reference to `get_long' collect2: error: ld returned 1 exit status``` You don't have get_long defined anywhere. Without more info, that is all that one can tell you. 1 members found this post helpful.
 12-15-2022, 04:12 PM #5 teckk LQ Guru   Registered: Oct 2004 Distribution: Arch Posts: 5,190 Blog Entries: 6 Rep: I replaced: Code: ```//x = get_long("Number: "); printf("Number: \n"); scanf("%d", &x);``` And it compiled and runs. Code: ```./test Number: 12345678 INVALID``` I don't know what get_long is suppose to do.
12-15-2022, 04:57 PM   #6
rnturn
Senior Member

Registered: Jan 2003
Location: Illinois (SW Chicago 'burbs)
Distribution: openSUSE, Raspbian, Slackware. Previous: MacOS, Red Hat, Coherent, Consensys SVR4.2, Tru64, Solaris
Posts: 2,833

Rep:
Quote:
 Originally Posted by lxs602 In the wrong place / incorrectly assigned?
What happens if you move the declaration of "x" down into the top of main() rather than declaring it at the top of the entire file? (Caveat: my C is rusty but I've never been keen on using variables that look to be global. My feelings won't be hurt if this doesn't help.)

 12-15-2022, 05:48 PM #7 GazL LQ Veteran   Registered: May 2008 Posts: 6,975 Rep: Use of those floating point literals makes me nervous. Not all numbers can be represented as a floating point value and using them forces the implicit conversion of 'x' into a double for any expressions using one of them as an operand. Much safer to stick with longs. The first positive integer number that is unrepresentable as a 64bit double is over 9e+15, so most card numbers are likely safe, but "1e16 - 1" might not be! Lets see... Code: ```\$ cat float.c #include int main() { printf("%f\n", 1e16 - 1 ); printf("%f\n", 56e14 - 1 ); return 0; } \$ cc -Wall float.c && ./a.out 10000000000000000.000000 5599999999999999.000000 \$``` As you can see, the 1e16 - 1 gets rounded back up to 1e16. So, if your only goal for using the exponent form is to make the code more readable by avoiding those long numbers cluttering up your expressions then you'd be much better off using a macro instead, something like: #define L_1e14 100000000000000L This will keep the code readable while avoiding all the nastiness that using floating point operations introduces. 1 members found this post helpful.
12-15-2022, 05:59 PM   #8
GazL
LQ Veteran

Registered: May 2008
Posts: 6,975

Rep:
Quote:
 Originally Posted by teckk You don't have get_long defined anywhere. Without more info, that is all that one can tell you.
As it happens, I can remember watching part of the CS50 video lectures a few weeks back. I think the get_long() is just a wrapper around a simple scanf() with a little error catching thrown in to hide some of the ugliness from the beginner students.

For the purpose of testing, a scanf("%ld", &x); will likely suffice.

1 members found this post helpful.
12-15-2022, 06:28 PM   #9
ntubski
Senior Member

Registered: Nov 2005
Distribution: Debian, Arch
Posts: 3,793

Rep:
Quote:
I downloaded https://github.com/cs50/libcs50/blob/v11.0.1/src/cs50.c as well, I'm able to reproduce the crash with a large enough number:

Code:
```\$ ./check-credit
Number: 4242424242424242
Floating point exception```
Quote:
 Originally Posted by lxs602 I also tried using gdb, but it gave an error to do with get_long being not declared.
Do you not have the headers in the right place? It's really important to get a debugger working for problems like this:

Code:
```Program received signal SIGFPE, Arithmetic exception.
0x00005555555552fe in checkfunction () at check-credit.c:31
31                      y = (x % (i * 10)) + y;
(gdb) print i
\$4 = -2147483648
(gdb) print/x i
\$5 = 0x80000000```

1 members found this post helpful.
 12-16-2022, 11:45 AM #10 sundialsvcs LQ Guru   Registered: Feb 2004 Location: SE Tennessee, USA Distribution: Gentoo, LFS Posts: 10,776 Blog Entries: 4 Rep: I would design this function to use a string input which it parses character by character, converting each to a digit with x = ord(ch) - ord('0'); after testing that the character is, in fact, a digit. This would allow the input of spaces which the loop could simply ignore. You can count the digits to ensure that the length is correct. Your code as-written assumes that a credit card number can be represented as long but this is not a valid assumption. Last edited by sundialsvcs; 12-16-2022 at 11:47 AM. 1 members found this post helpful.
12-16-2022, 01:59 PM   #11
teckk
LQ Guru

Registered: Oct 2004
Distribution: Arch
Posts: 5,190
Blog Entries: 6

Rep:
Quote:
 I think the get_long() is just a wrapper around a simple scanf() with a little error catching...
Oh ok, get_long is here, finally had a chance to look at it.

https://github.com/cs50/libcs50/blob/v11.0.1/src/cs50.c
Line 369

1 members found this post helpful.
 12-16-2022, 02:27 PM #12 teckk LQ Guru   Registered: Oct 2004 Distribution: Arch Posts: 5,190 Blog Entries: 6 Rep: Ok, to put all of that into one c file, so that you can look at it, and study it, without the need to include header files from a different location, and to see what it is doing. aaa.c Code: ```#include #include #include #include #include #include #include #include #include #include #include #include #include #include //#define _GNU_SOURCE long x; int y = 0; int i = 1; typedef char *string; static size_t allocations = 0; static string *strings = NULL; char get_char(const char *format, ...) __attribute__((format(printf, 1, 2))); double get_double(const char *format, ...) __attribute__((format(printf, 1, 2))); float get_float(const char *format, ...) __attribute__((format(printf, 1, 2))); int get_int(const char *format, ...) __attribute__((format(printf, 1, 2))); long get_long(const char *format, ...) __attribute__((format(printf, 1, 2))); long long get_long_long(const char *format, ...) __attribute__((format(printf, 1, 2))); string get_string(va_list *args, const char *format, ...) __attribute__((format(printf, 2, 3))); string get_string(va_list *args, const char *format, ...) { if (allocations == SIZE_MAX / sizeof (string)) { return NULL; } string buffer = NULL; size_t capacity = 0; size_t size = 0; int c; if (format != NULL) { va_list ap; if (args == NULL) { va_start(ap, format); } else { va_copy(ap, *args); } vprintf(format, ap); va_end(ap); } while ((c = fgetc(stdin)) != '\r' && c != '\n' && c != EOF) { if (size + 1 > capacity) { if (capacity < SIZE_MAX) { capacity++; } else { free(buffer); return NULL; } string temp = realloc(buffer, capacity); if (temp == NULL){ free(buffer); return NULL; } buffer = temp; } buffer[size++] = c; } if (size == 0 && c == EOF) { return NULL; } if (size == SIZE_MAX) { free(buffer); return NULL; } if (c == '\r' && (c = fgetc(stdin)) != '\n') { if (c != EOF && ungetc(c, stdin) == EOF) { free(buffer); return NULL; } } string s = realloc(buffer, size + 1); if (s == NULL) { free(buffer); return NULL; } s[size] = '\0'; string *tmp = realloc(strings, sizeof (string) * (allocations + 1)); if (tmp == NULL) { free(s); return NULL; } strings = tmp; strings[allocations] = s; allocations++; return s; } long get_long(const char *format, ...) { va_list ap; va_start(ap, format); while (true) { string line = get_string(&ap, format); if (line == NULL) { va_end(ap); return LONG_MAX; } if (strlen(line) > 0 && !isspace((unsigned char) line[0])) { char *tail; errno = 0; long n = strtol(line, &tail, 10); if (errno == 0 && *tail == '\0' && n < LONG_MAX) { va_end(ap); return n; } } } } int checkfunction(void) { printf("Made it to point A\n"); //Point A for (i = i + 0; i <= x; i *= 1e2) { int z; z = x % 10; if (z <= 4) { y = z * 2 + y; } else if (z >= 6) { y = ( z * 2 ) - 10 + 1 + y; } y = (x % (i * 10)) + y; } return y; } int main(void) { x = get_long("Number: "); if (x < 1e12 || x > 1e16 - 1 || (x >= 1e13 && x <= 1e14)) { printf("INVALID A\n"); //Inv A } else { if (x > 1e14 && x < 1e15 - 1) { if ((x < 34e13 && x > 1e14 - 1) || (x > 37e13 && x < 38e13 - 1)) { checkfunction(); if ((y % 10) == 0) { printf("AMEX\n"); } else { printf("INVALID B\n"); //Inv B } } } else if (x > 4e12 && x < 5e12 - 1) { checkfunction(); if ((y % 10) == 0) { printf("VISA\n"); } else { printf("INVALID C\n"); //Inv C } } else if (x > 4e15 && x < 5e15 - 1) { i = 1; checkfunction(); if ((y % 10) == 0) { printf("VISA\n"); } else { printf("INVALID D\n"); //Inv D } } else if (x > 51e14 && x < 56e14 - 1) { printf("Made it to this point B\n"); //Point B checkfunction(); printf("%i", y); if ((y % 10) == 0) { printf("MASTERCARD\n"); } else { printf("INVALID E\n"); //Inv E } } else { printf("INVALID F\n"); //Inv F } } }``` Code: `gcc aaa.c -o aaa` Code: ```./aaa Number: 111111111111 INVALID A ./aaa Number: 1111111111111 INVALID F ./aaa Number: 111111111111111 Made it to point A Floating point exception (core dumped)``` Last edited by teckk; 12-16-2022 at 02:30 PM.
 12-16-2022, 07:26 PM #13 sundialsvcs LQ Guru   Registered: Feb 2004 Location: SE Tennessee, USA Distribution: Gentoo, LFS Posts: 10,776 Blog Entries: 4 Rep: Let me kindly repeat: your true input is a string, which must consist of digit-characters and spaces, and which must contain a specified number of digit-characters. Do not try to use a library call to convert this input into "an integer" of whatever length, because in the general case it will not work. You need to step back and reconsider your entire approach. 1 members found this post helpful.
 12-17-2022, 05:24 AM #14 teckk LQ Guru   Registered: Oct 2004 Distribution: Arch Posts: 5,190 Blog Entries: 6 Rep: 1 members found this post helpful.
 12-17-2022, 10:43 AM #15 NevemTeve Senior Member   Registered: Oct 2011 Location: Budapest Distribution: Debian/GNU/Linux, AIX Posts: 4,919 Blog Entries: 1 Rep: Code: ```\$ diff -u cs50.bak cs50.c --- cs50.bak 2022-12-17 17:26:48.111099200 +0100 +++ cs50.c 2022-12-17 17:41:56.737257200 +0100 @@ -6,7 +6,7 @@ // y is the calculated checksum int y = 0; // i is an integer for calculations -int i = 1; +long int i = 1; /* move this definition into 'checkfunction' */ // Function to loop through the digits of the card number, beginning with the last digit. See HP Luhn algorithm for further details int checkfunction(void)```

 Posting Rules You may not post new threads You may not post replies You may not post attachments You may not edit your posts BB code is On Smilies are On [IMG] code is Off HTML code is Off Forum Rules

 Similar Threads Thread Thread Starter Forum Replies Last Post Godsmacker777 Programming 11 03-17-2005 11:35 AM Boffy Programming 5 09-09-2004 12:44 PM CragStar Programming 2 01-21-2001 09:19 AM

LinuxQuestions.org

All times are GMT -5. The time now is 04:07 PM.

 Contact Us - Advertising Info - Rules - Privacy - LQ Merchandise - Donations - Contributing Member - LQ Sitemap -