Is this code for calculating Code128 barcode check digits correct?

B. Clay Shannon picture B. Clay Shannon · Sep 10, 2013 · Viewed 11k times · Source

Based on my understanding* of the check digit calculation for Code128 barcodes (which differs radically/drastically from most of the other standard barcode types), my code below is correct. However, I would appreciate a "sanity check" by those who may know better/have some empirical observations/mud from the trenches attached to their trench (or lab) coats.

  • This is my understanding:

Each character in the barcode, from the left, is first converted into its ASCII code (IOW, a barcode character of "1" is to be viewed as its ASCII Code of 49, etc.) and then that value is multiplied by its ordinal position in the array of chars.

e.g. for the fabricated barcode number "123456789" the "1" equates to 49, the "2" to 50, ... the "9" to 57.

Then, the ordinal position of each character is multiplied by its ASCII value. e.g., 1*49 == 49, 2*50==100, ... 9*57==513. You then sum all these up, to arrive at (in this case) 2,445.

The next step is to divide that number by the Code128 "magic number" of 103; the value you're interested in is the modulus. So, in this case, 2445 % 103 == 76.

Penultimate-finally, you convert that value (76), considering it to be an ASCII code, back the other direction to its "presentation" value, namely "L".

Finally, you append that calculated character to the original barcode. Verily and thus (notwithstanding further ado or adieux), you end up with a value of "123456789L"

If this is not the correct value, then I'm understanding something incorrectly.

Here are a few barcodes:

0) 123456789
1) 12345678
2) 1234567
3) 123456

...and how they should appear with their calculated check digits (and do, with the code below):

0) 123456789L
1) 12345678N
2) 1234567*
3) 123456E

Last but not least, here is the code I used to compute the Code128 check digits:

private void buttonAppendCode128CheckDigit_Click(object sender, EventArgs e)
{
    const int CODE_128_DIVISOR = 103;
    string barCode = textBoxRawCode128.Text.Trim();
    int runningTotal = 0;

    for (int i = barCode.Length - 1; i > -1; i--)
    {
        char valToConvertToASCII = Convert.ToChar(barCode[i]);
        int valToMultiply = ConvertToASCIIInt(valToConvertToASCII);
        runningTotal += (valToMultiply*(i + 1));
    }

    int code128Modulus = runningTotal%CODE_128_DIVISOR;
    textBoxCode128WithCheckDigit.Text = barCode + ConvertToASCIIChar(code128Modulus);
}

private char ConvertToASCIIChar(int code128Modulus)
{
    return (char) code128Modulus;
}

private int ConvertToASCIIInt(char valToConvertToASCII)
{
    return valToConvertToASCII;
}

UPDATE

I'm not quite grokking Brian Anderson's answer; he may be right (probably is), but I would think the start and stop bits would be ignored as far as the check digit calculation goes. Aren't they there just so that the barcode scanner knows from which point to pay attention and thereafter from which point it can resume its electronic snoozing?

And then the math (provided Brian is correct about needing to subtract 32 from the ASCII vals) would be:

(17*1)+(18*2)+(19*3)+(20*4)+(21*5)+(22*6)+(23*7)+(24*8)+(25*9)
-or:
17 + 36 + 57 + 80 + 105 + 132 + 161 + 192 + 225 == 1005

Why would the start character be included in the calculation, but not the stop character?

Since 1005 % 103 == 78, the check digit would be ... "N" ... or would it be (78-32 == 46) "-"?

If both the stop and start characters were included, then of course that would change the solution, too...

UPDATE 2

I admit I'm not exactly an ogler of barcodes, so I may have seen such and just not noticed/paid attention, but can barcodes have such check digits as "-" and ""? It seems bizarre; I would expect them to always be alphanumerics if not just numerics. If my suspicion is correct, what is done when a calculation does end up with a squirrely check digit such as "-" or "" or "~", &c?

UPDATE 3

So, if I understand Brian and other sources I'v read correctly, I will read from the barcode scanner what semantically decodes to:

[startChar]123456789[checkDigit][stopChar]

...but which would logically strip out the stop char --since it is not part of the check digit calculation -- and (not quite so logically) also strip out the check digit, so what I actually get would be:

[startChar]123456789

...and massage it and calculate the check digit to to display a human-readable representation of the barcode:

123456789[checkDigit]

And since the start char has obviously (?) been seen if the barcode has scanned, I can just pre-add it to the runningTotal value to be computed; thus, my code is now:

private void buttonAppendCode128CheckDigit_Click(object sender, EventArgs e)
{
    const int CODE_128_DIVISOR = 103;
    string barCode = textBoxRawCode128.Text.Trim();
    int runningTotal = ConvertToASCIIInt(barcode[0]); // Start with the value of the start char; this should always be either 103 (Code128A), 104 (Code128B), or 105 (Code128C); 106 is the stop char

    for (int i = barCode.Length - 1; i > 0; i--) // now disregarding already calculated first element by ignoring element 0
    {
        char valToConvertToASCII = Convert.ToChar(barCode[i]);
        int valToMultiply = ConvertToASCIIInt(valToConvertToASCII);
        runningTotal += (valToMultiply*(i + 1));
    }

    int code128Modulus = runningTotal%CODE_128_DIVISOR;
    textBoxCode128WithCheckDigit.Text = barCode + ConvertToASCIIChar(code128Modulus);
}

private char ConvertToASCIIChar(int code128Modulus)
{
    return (char) code128Modulus;
}

private int ConvertToASCIIInt(char valToConvertToASCII)
{
    const int ASCII_ADJUSTMENT_VAL = 32;
    return valToConvertToASCII-ASCII_ADJUSTMENT_VAL;
}

Answer

Brian Anderson picture Brian Anderson · Sep 11, 2013

It looks like you might be missing the start character for code 128A (103), 128B (104) or 128C (105). This number is weighted with a '*1', just like the first character in your barCode string. I also think you have to do the math with the Code 128 values (i.e. ASCII_value_of_character - 32). So for the string "123456789" I get "104, 17, 18, 19, 20, 21, 22, 23, 24, 25, 79, 106", including the Start 128B (104), Stop (106) and a Checksum of 79 (1109 % 103).