0
votes

I'm trying to write code for Credit Problem set in CS50 (Credit), i'm at the very beginning but here's a problem I came across. Right now I'm trying to extract card digits to operate with them later and checking if the extraction went well. Code seems to work for every digit in a card number except for the 12th (starting from the end).

e.g. Card number: input: 1111111111111111 output:1111111111101111

Though in some cases this problem doesn't occur:

input: 1234567890123456 output: 1234567890123456

Here's my code (please don't be too harsh, I'm a newbie:)) I'm aware that there are other ways to get those digits, but I just really want to now why it doesn't work. Thanks in advance!

#include <stdio.h>
#include <cs50.h>

long card_no(void);
int main(void)
{
    long card = card_no();
    int one = (card % 10);
    int two = (card % 100 - card % 10) * 0.1;
    int tree = (card % 1000 - card % 100) * 0.01;
    int four = (card % 10000 - card % 1000) * 0.001;
    int five = (card % 100000 - card % 10000) * 0.0001;
    int six = (card % 1000000 - card % 100000) * 0.00001;
    int seven = (card % 10000000 - card % 1000000) * 0.000001;
    int eight = (card % 100000000 - card % 10000000) * 0.0000001;
    int nine = (card % 1000000000 - card % 100000000) * 0.00000001;
    int ten = (card % 10000000000 - card % 1000000000) * 0.000000001;
    int eleven = (card % 100000000000 - card % 10000000000) * 0.0000000001;
    int twelve = (card % 1000000000000 - card % 100000000000) * 0.00000000001;
    int thirteen = (card % 10000000000000 - card % 1000000000000) * 0.000000000001;
    int forteen = (card % 100000000000000 - card % 10000000000000) * 0.0000000000001;
    int fifteen = (card % 1000000000000000 - card % 100000000000000) * 0.00000000000001;
    int sixteen = (card % 10000000000000000 - card % 1000000000000000) * 0.000000000000001;

    printf("%i%i%i%i%i%i%i%i%i%i%i%i%i%i%i%i\n", sixteen, fifteen, forteen, thirteen, twelve, eleven, ten, nine, eight, seven, six, five, four, tree, two, one);

}

//Prompt user for 13 to 16 digits card number
long card_no(void)
{
    long n;
    do
    {
       n = get_long("Card number:\n"); 
    }
    while (n < 1000000000000 || n > 9999999999999999);
    return n;
}
2
Maybe the card number won't fit in a long. Why not process as a string, getting each digit separately? - Paul Ogilvie
Or there can be imprecision when you multiply with a floating point number. Why not use integer division, /? - Paul Ogilvie
What exactly is the program supposed to do? Anyway it looks totally weird. - Jabberwocky

2 Answers

1
votes

Floating point math isn't exact - values like 0.1 cannot be represented exactly in a finite number of bits, so you only get an approximation like 0.099... or 0.1000...1 or something like that. So after doing the math for your fifth digit you wind up with a value of 0.9..., which gives 0 when converted to an int.

Another option that doesn't rely on floating point math is to divide the number by increasing magnitude and taking the % 10 of the result. IOW,

(123 /   1) % 10  == 123 % 10 == 3
(123 /  10) % 10  ==  12 % 10 == 2
(123 / 100) % 10  ==   1 % 10 == 1

Integer division yields an integer result - any fractional portion is discarded.

Handy hint, when you find yourself creating a bunch of variables of the same type with ordinal names (first, second, third or one, two, three) that's a really strong hint that you want to use an array:

#define  NUM_DIGITS 16
int digits[NUM_DIGITS];

/** 
 * get card number as before
 */

long magnitude = 1;
/**
 * Arrays are indexed from 0 to N-1
 */
for ( size_t i = 0; i < NUM_DIGITS; i++ ) 
  digits[i] = (card / magnitude) % 10;
  magnitude *= 10;
}

/**
 * I typically use size_t for array indices, which is an unsigned type.
 * This means it cannot represent negative values.  If we loop while "i >= 0",
 * then the loop will never exit, since subtracting 1 from 0 will "wrap
 * around" to the largest value.  So even though the array is indexed from
 * NUM_DIGITS-1 to 0, we loop over NUM_DIGITS to 1 and subtract 1 from 
 * i in the subscript operation.
 */
for ( size_t i = NUM_DIGITS; i > 0; i-- ) 
  printf( "%d", digits[i-1] );
0
votes

This little bit of code might show you a MUCH easier way to get going.
It extracts all the digits of a number, one at a time, without using increasingly gigantic numbers like 10000000.

#include <stdio.h>

int main(void) {
    unsigned long card_number = 564879165464;
    int counter = 1;

    while(card_number)
    {
        printf("Digit %d = %d\n", counter++, card_number % 10);
        card_number /= 10;
    }

    return 0;
}

Output

Digit 1 = 4
Digit 2 = 6
Digit 3 = 4
Digit 4 = 5
Digit 5 = 6
Digit 6 = 1
Digit 7 = 9
Digit 8 = 7
Digit 9 = 8
Digit 10 = 4
Digit 11 = 6
Digit 12 = 5