BradleyRobertR
BradleyRobertR

Reputation: 55

Using CheckSum with C++ for 13 Digit ISBN

I am trying to calculate the final digit of a 13 digit ISBN using the first 12 digits using C++. I feel like my code should be correct but I have a feeling the formula I'm using may be wrong.

The formula is:

10 - (d0 + d1 * 3 + d2 + d3 * 3 + d4 + d5 * 3 + d6 + d7 * 3 + d8 + d9 * 3 + d10 + d11 * 3) % 10

Here's what I have:

#include <cstring>
#include <iostream>

int main() {
    int weightedSum = 0;
    int checksum = 0;
    int i;      //for loop decrement
    int mul = 3;
    const int LENGTH = 12;
    char ISBNinput[LENGTH];

    std::cout << "Enter first 12 digits of ISBN: ";   //ask user for input
    std::cin >> ISBNinput;       //stores input into ISBNinput
    std::cout << std::endl;

    for (i = 0;  i < strlen(ISBNinput);  i++) {

        weightedSum += (ISBNinput[i] % 12) * mul;

        if (mul == 3) {
            mul = 1;
        } else {
            mul = 3;
        }

    }//close for loop

    checksum = weightedSum % 10;        //calculates checksum from weightedSum

    std::cout << checksum << std::endl;       //prints checksum with new line for format

    return 0;
}

For example:

978007063546 should return 3

and

978032133487 should return 9

Thank you for any help.

Upvotes: 1

Views: 4210

Answers (2)

Toby Speight
Toby Speight

Reputation: 30831

Here's how I go about this.

First, let's decide how we're going to test this. I'll assume that we've written the function, and that it gives the correct output. So I pick up a couple of books off my desk, and test that it works for them:

#include <iostream>

int main()
{
    std::cout << "Book 1 - expect 3, got " << checksum("978032114653") << std::endl;
    std::cout << "Book 2 - expect 0, got " << checksum("978020163361") << std::endl;
}

Of course, when we try to compile that, we get an error. So create the function, before main():

char checksum(const char *s)
{
    return '1';
}

Now it compiles, but the result is always 1, but now we can start to fill in the body. Let's start with some smaller examples, that we can calculate by hand; add these at the beginning of main():

    std::cout << "1 digit - expect 4, got " << checksum("6") << std::endl;

Now let's get this one working - this gives us conversion from character to digit and back, at least:

char checksum(const char *s)
{
    int digit = *s - '0';
    return '0' + 10 - digit;
}

Let's try 2 digits:

    std::cout << "1 digit - expect 6, got " << checksum("11") << std::endl;

And now our test fails again. So add some more processing, to make this pass (and not break the single-digit test):

char checksum(const char *s)
{
    int sum = 0;
    int digit = *s - '0';
        sum += digit;
    ++s;
    if (*s) {
        digit = *s - '0';
        sum += 3 * digit;
    }
    return '0' + (10 - sum)%10;
}

We're probably ready to make this into a loop now. Once that's passed, we no longer need the short tests, and I have:

#include <iostream>

char checksum(const char *s)
{
    int sum = 0;
    for (int mul = 1;  *s;  ++s) {
        int digit = *s - '0';
        sum += mul * digit;
        mul = 4 - mul;
    }
    return '0' + (1000 - sum)%10;
}

int test(const char *name, char expected, const char *input)
{
    char actual = checksum(input);
    if (actual == expected) {
        std::cout << "PASS: " << name << ": "
                  << input << " => " << actual
                  << std::endl;
        return 0;
    } else {
        std::cout << "FAIL: " << name << ": "
                  << input << " => " << actual
                  << " - expected " << expected
                  << std::endl;
        return 1;
    }
}

int main()
{
    int failures = 0;
    failures += test("Book 1", '3', "978032114653");
    failures += test("Book 2", '0', "978020163361");
    return failures > 0;
}

I factored out the actual checking into a function here, so we can keep count of failures, and exit with the appropriate status, but everything else is as I described above.

You'll want to add a few more test cases - in particular, make sure the function correctly returns the extreme values 0 and 9 when it should.

Upvotes: 2

There is one clear bug in your code: you are not allocating enough space in for ISBNinput. You should make it one character longer:

const int LENGTH = 13;

The reason for this is that that character-array strings are terminated with an extra null character. You might be lucky and the next byte in memory could sometimes happen to be a null byte, in which case the program would still work sometimes.

If you run the program with valgrind or a similar memory checker you are likely to see an error as the program access memory beyond what was allocated on the stack.

Also I think there is another bug. I think that mul should be initialized to 1.

By the way, this code is very fragile, depending on you entering no more than 12 characters, all of which are assumed to be digits. It might be OK as a quick hack for a proof-of-concept, but should not be used in any real program.

Upvotes: 1

Related Questions