*UPDATED*
I just discovered a bug in the check digit calculation, fixed below. Essentially, the check digit is only subtracted from 10 if the sum_of_totals % 10 is greater than 0. I'm sorry for any inconvenience!
I recently had a project where I had to use a barcode scanner to make scans of numbers that were possibly smaller than the 13 digit EAN standard. I had to then check these against the same number in a list, but the numbers in the list had been expanded to 13 digits using the algorithm below:
If the scanned number has 13 or more digits, return it.
If the scanned number has 12 digits, prepend a zero and return the result.
If the scanned number had 6 digits, treat it as a Zero Compressed UPC-E, expand it 11 digits using the following table and continue:
Last digit | UPC-E equivalent is | UPC-A equivalent is |
0 | XXNNN0 | 0XX000-00NNN + check |
1 | XXNNN1 | 0XX100-00NNN + check |
2 | XXNNN2 | 0XX200-00NNN + check |
3 | XXXNN3 | 0XXX00-000NN + check |
4 | XXXXN4 | 0XXXX0-0000N + check |
5 | XXXXX5 | 0XXXXX-00005 + check |
6 | XXXXX6 | 0XXXXX-00006 + check |
7 | XXXXX7 | 0XXXXX-00007 + check |
8 | XXXXX8 | 0XXXXX-00008 + check |
9 | XXXXX9 | 0XXXXX-00009 + check |
If the number (as scanned or expanded) has less than 12 digits, left pad it to 12 digits using zeros, add a check digit, and return the result.
The check digit is computed as follows:
- Sum the digits in all odd positions, and multiply the result by 3.
- Sum the digits in all even positions.
- Sum the totals calculated in steps 2 and 3.
- *UPDATED* Check digit is sum_of_totals % 10 > 0 ? 10 - (sum_of_totals % 10) : 0
Here is my C# program that I used to work this out:
using System;
using System.Collections.Generic;
using System.Text;
namespace ConsoleApplication1 {
class Program {
protected static int[][] upcPartialChecksumDigit = new int[][]{
new int[] { 0 * 1, 1 * 1, 2 * 1, 3 * 1, 4 * 1, 5 * 1, 6 * 1, 7 * 1, 8 * 1, 9 * 1 }, // 0
new int[] { 0 * 3, 1 * 3, 2 * 3, 3 * 3, 4 * 3, 5 * 3, 6 * 3, 7 * 3, 8 * 3, 9 * 3 }, // 1
new int[] { 0 * 1, 1 * 1, 2 * 1, 3 * 1, 4 * 1, 5 * 1, 6 * 1, 7 * 1, 8 * 1, 9 * 1 }, // 2
new int[] { 0 * 3, 1 * 3, 2 * 3, 3 * 3, 4 * 3, 5 * 3, 6 * 3, 7 * 3, 8 * 3, 9 * 3 }, // 3
new int[] { 0 * 1, 1 * 1, 2 * 1, 3 * 1, 4 * 1, 5 * 1, 6 * 1, 7 * 1, 8 * 1, 9 * 1 }, // 4
new int[] { 0 * 3, 1 * 3, 2 * 3, 3 * 3, 4 * 3, 5 * 3, 6 * 3, 7 * 3, 8 * 3, 9 * 3 }, // 5
new int[] { 0 * 1, 1 * 1, 2 * 1, 3 * 1, 4 * 1, 5 * 1, 6 * 1, 7 * 1, 8 * 1, 9 * 1 }, // 6
new int[] { 0 * 3, 1 * 3, 2 * 3, 3 * 3, 4 * 3, 5 * 3, 6 * 3, 7 * 3, 8 * 3, 9 * 3 }, // 7
new int[] { 0 * 1, 1 * 1, 2 * 1, 3 * 1, 4 * 1, 5 * 1, 6 * 1, 7 * 1, 8 * 1, 9 * 1 }, // 8
new int[] { 0 * 3, 1 * 3, 2 * 3, 3 * 3, 4 * 3, 5 * 3, 6 * 3, 7 * 3, 8 * 3, 9 * 3 }, // 9
new int[] { 0 * 1, 1 * 1, 2 * 1, 3 * 1, 4 * 1, 5 * 1, 6 * 1, 7 * 1, 8 * 1, 9 * 1 }, // 10
new int[] { 0 * 3, 1 * 3, 2 * 3, 3 * 3, 4 * 3, 5 * 3, 6 * 3, 7 * 3, 8 * 3, 9 * 3 } // 11
};
/*
Note 6 digit UPC-E codes are expanded to 12 digit UPC-A codesas follows:
0 XXNNN0 00XX000-00NNN + check
1 XXNNN1 00XX100-00NNN + check
2 XXNNN2 00XX200-00NNN + check
3 XXXNN3 00XXX00-000NN + check
4 XXXXN4 00XXXX0-0000N + check
5 XXXXX5 00XXXXX-00005 + check
6 XXXXX6 00XXXXX-00006 + check
7 XXXXX7 00XXXXX-00007 + check
8 XXXXX8 00XXXXX-00008 + check
9 XXXXX9 00XXXXX-00009 + check
*/
public static string getUPC(string id) {
if (id == null) return null;
StringBuilder s = new StringBuilder("0", 11);
if (id.Length == 6) {
switch (id[5] - '0') {
case 0:
id = s.Append(id[0]).Append(id[1]).Append("00000").Append(id[2]).Append(id[3]).Append(id[4]).ToString();
break;
case 1:
id = s.Append(id[0]).Append(id[1]).Append("10000").Append(id[2]).Append(id[3]).Append(id[4]).ToString();
break;
case 2:
id = s.Append(id[0]).Append(id[1]).Append("20000").Append(id[2]).Append(id[3]).Append(id[4]).ToString();
break;
case 3:
id = s.Append(id[0]).Append(id[1]).Append(id[2]).Append("00000").Append(id[3]).Append(id[4]).ToString();
break;
case 4:
id = s.Append(id[0]).Append(id[1]).Append(id[2]).Append(id[3]).Append("00000").Append(id[4]).ToString();
break;
case 5:
id = s.Append(id[0]).Append(id[1]).Append(id[2]).Append(id[3]).Append(id[4]).Append("00005").ToString();
break;
case 6:
id = s.Append(id[0]).Append(id[1]).Append(id[2]).Append(id[3]).Append(id[4]).Append("00006").ToString();
break;
case 7:
id = s.Append(id[0]).Append(id[1]).Append(id[2]).Append(id[3]).Append(id[4]).Append("00007").ToString();
break;
case 8:
id = s.Append(id[0]).Append(id[1]).Append(id[2]).Append(id[3]).Append(id[4]).Append("00008").ToString();
break;
case 9:
id = s.Append(id[0]).Append(id[1]).Append(id[2]).Append(id[3]).Append(id[4]).Append("00009").ToString();
break;
default:
return null;
}
}
if (id.Length < 12) {
id = id.PadLeft(12, '0');
int checksum = 0;
for (int i = 0; i < 12; i++) { //should get unrolled by compiler hopefully
int c = id[i] - '0';
if (c < 0 || c > 9) return null;
int partial = upcPartialChecksumDigit[i][c];
checksum += partial;
}
// *UPDATED*
char checksumDigit = '0';
checksum %= 10;
if(checksum > 0) checksumDigit += (char)(10 - checksum);
id += checksumDigit;
}
else if (id.Length == 12) {
id = '0' + id;
}
return id;
}
static void Main(string[] args) {
string s = ConvertUPC("654321");
Console.WriteLine();
}
public static String ConvertUPC(string arg) {
return arg + " -> " + getUPC(arg);
}
}
}