0

I'm new to programming, C and I'm trying to solve CS50's pset1 problem, Luhn's Algorithm to check if a credit card is valid. The example I'm using to test is this card Visa: 4003600000000014.

As referred in the problem, first underline every other digit, starting with the number’s second-to-last digit:

[4] 0 [0] 3 [6] 0 [0] 0 [0] 0 [0] 0 [0] 0 [1] 4

Multiply the underlined digits by 2:

1•2 + 0•2 + 0•2 + 0•2 + 0•2 + 6•2 + 0•2 + 4•2

That gives:

2 + 0 + 0 + 0 + 0 + 12 + 0 + 8

Add those products’ digits (i.e., not the products themselves) together:

2 + 0 + 0 + 0 + 0 + 1 + 2 + 0 + 8 = 13
#include <cs50.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>

int main(void)
{

    // Variable assignment
    long card;
    int case1, case2, mod, mod2;

    // Get input from user
    do
    {
        card = get_long("Card: ");
    }
    while (card <= 0);

    // Case 1
        for (long checksum = card; checksum > 0; checksum = checksum / 10)
    {

        mod = remainder(checksum, 10) * 2;
        if (mod >= 10)
        {
            mod2 = remainder (mod, 10);
            case1 = case1 + mod2;
            mod = mod / 10;
            case1 = case1 + mod;
        }
        else
        {
            case1 = case1 + mod;
        }
        checksum = checksum / 10;
        printf("\n%d", case1);
    }
}

My output:

~/pset1/credit/ $ ./credit 
Card: 4003600000000014

8
8
8
8
8
8
14
14

The number printed should be 13, for some reason the number my output is printing is 14. I've tried to make changes to the code, although i get outputs of 2 and -6. Tried CS50 debug tool too, without success. What is my code missing in order to have an output of 13?

var-log
  • 31
  • 5
  • Are you sure that `long` holds enough digits? It can be 32-bit. Better to use a string for card numbers, phone numbers anyway. It is kind of perverse to enter digit by digit, convert to an integer, and then convert back to individual digits, and you lose crucial leading zeros. – Weather Vane Jun 22 '21 at 18:59
  • Did it only loop 8 times? – Weather Vane Jun 22 '21 at 19:02
  • @var-log `long long` is enough, but as they said, it's better to store it in a string rather than a number. It makes everything else much easier. – Barmar Jun 22 '21 at 19:07
  • @WeatherVane , i'll give it a check and try to learn how to store it using string. – var-log Jun 22 '21 at 19:15
  • @Barmar used long for convenience, easier for a beginner to handle the operators and number manipulation. although i'll try to understand how to manipulate numbers with strings – var-log Jun 22 '21 at 19:16
  • I believe your other problem is that you're throwing away half of the digits. [Luhn's algorithm](https://en.wikipedia.org/wiki/Luhn_algorithm) is supposed to operate on all the digits: summing twice the odd digits, but 1x the even digits. – Steve Summit Jun 22 '21 at 19:44
  • Also the checksum you compute does *not* give the check digit directly. Roughly speaking, you take 10 minus the last digit of the checksum. For the input number 4003600000000014, the correct checksum is 2 + 1+2 + 3 + 8 = 16, and then 10 - 6 gives 4. – Steve Summit Jun 22 '21 at 20:00
  • it has been solved! thanks anyway. @SteveSummit – var-log Jun 22 '21 at 20:25

2 Answers2

2

You're starting with the last digit instead of the next-to-last digit, so you're working with the wrong set of digits.

You need to start with the number / 10 to grab the next-to-last number.

for (long checksum = card / 10; checksum > 0; checksum = checksum / 10)
dbush
  • 205,898
  • 23
  • 218
  • 273
  • thanks! i was in doubt about where it was starting... was not sure if the last argument would run by the start or only when needed to loop tried with a while loop to divide it by 10 in the beginning and in the end. – var-log Jun 22 '21 at 19:17
  • @var-log Glad I could help. Feel free to [accept this anwser](https://stackoverflow.com/help/accepted-answer) if you found it useful. – dbush Jun 22 '21 at 19:18
  • 1
    This change alone should not fix the problem; the `remainder` function returns negative values for the sample data, so the result should still be wrong. Also, `case1` is not initialized. – Eric Postpischil Jun 22 '21 at 19:25
2
  1. This is not a minimal reproducible example. get_long is not a standard C function, so you must have included another header to get this code to compile. When requesting debugging help, always include a complete example.

  2. case1 is not initialized. Initialize it to zero.

  3. checksum is initialized to card, and the loop examines every other digit starting with the last. So it operates on the digits you do not want to add in this way. Change the loop so checksum starts with card/10.

  4. remainder is a double function. It is generally ill-advised to mix floating-point and integer operations, especially if you are unfamiliar with floating-point. Calling remainder will convert its operands to double, and this may cause rounding errors, in which case an undesired result will be produced.

  5. remainder provides a symmetric remainder; it returns a value in [−y/2, +y/2], where y is the second operand. You do not want negative values. Use the integer remainder operator, %, instead.

  6. Avoid using integer types for credit card “numbers.” Credit card “numbers” are more properly identifiers that are strings of digits—their numerical value is not relevant to their purpose (for example, they are not a count of how many cards have been issued), and, in some C implementations, the long type is not wide enough to represent the entire “number.” An array of characters would suffice, although you would have to convert the digit characters (“0” to “9”) to digit values ('0' to '9'), which you may do by subtracting the character constant '0' from the digit character code.

Do not print with "\n%d". Arrange your outputs to have the new-line character last except in cases where either you are about to take input and want the prompt on the same line as the input or you deliberately do not want the text necessarily output yet. Generally, standard output to a terminal (physical or virtual) is line-buffered, meaning output will not appear to the user until a new-line character is sent, the buffer is full, input is requested, or the buffer is manually flushed.

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
  • i guess i'll spend some time fixing my code based on the tips you presented. in using % might be the solution to achieve the right output as i'm having negative numbers with remainder. didn't knew it gives negatives, used it because of some reason i was getting an error with %... in any case i'll definitely give it another try. thanks for all the tips! – var-log Jun 22 '21 at 19:37
  • Solved ;) using the tips i managed to get to the right value. % made a huge difference! – var-log Jun 22 '21 at 20:26