20 December 2007

Calculating a Checkdigit using Math.Ceiling

Calculating Checkdigits


So, something I've been wanting to do is calculate a checkdigit for a UPC-A code in Code. In case you didn't know, the check digit is the final digit on a Standard 12 digits or 14 digit UPC and can be calculated from the preceding digits



The reasoning behind this is if a barcode reader scans a barcode wrong it will internally do a calculation and see if the result matches the checkdigit. If it matches all is good. The UPC is made up of a manufacturer code and a product code. So, for example: 0-12345-67890-5 :


  • 0-12345 marks the manufacturer

  • 67890 identifies the product

  • 5 is the check digit

Anyway, we want to enable people to enter 67890 as a check-digit, and then have the program automatically calculate the UPC to be entered in to the database.


To Calculate the Checkdigit “5” is fairly straight forward to do for human calculators (the brain) but presents some challenges for computers.

The calculation goes as follows,


  1. Add up all the odd position digits
  2. multiply that by 3
  3. add the even position digits
  4. Take the total and whatever you need to get to the nearest 10 is the checkdigit.
  5. To illustrate:
    1. 0-12345-67899-?
    2. (0 + 2 + 4 +6 + 8 + 9) * 3 = 87
    3. 87 + (1 + 3 + 5 + 7 + 9) = 112
    4. 112 + x = 120 (next multiple of 10)
    5. x = 8


Simple Right? It's basic addition and subtraction.


However, the computer doesn't know how to get 120 from.

However, essentially, what we want to do it go (10 - 2)

MODULUS to the rescue!!!



Modulus is basically an operator that lets you calculate the remainder. If you remember back in Grade school, before you learned fractions or decimals, 10 / 3 = 3 Remainder 1, 3R1

In C# the Modulus Operator is %

Well, If we take the total (112) and do (112 % 10) we end up with a remainder of 2... so All we need to do is this: 10 - (total % 10) = 8 DONE! Right?

But not so fast...



There is a problem. If we end up with a total which IS a multiple of 10, our remainder = 0 (110 % 10) = 0 Rem. And since 10 - 0 = 10, this won't work (the checkdigit should be 0, not 10)

So, just write an IF statement, right? If it's 10, set it to 0, if not, subtract from 10.

The if statement could be written like so:
int checkdigit = (total % 10 == 0)? 0 : 10 - (total % 10);

However, I prefer a purely mathematical approach if I can. The first approach we came up with used the Math.Ceiling function. What this function does is basically returns the next whole number. So, Math.Ceiling(11.2) would return 12.0. How can we use this?

Well, our total 112 / 10 actually = 11.2, If we take the Ceiling of that number, we end up with 12. and 12 x 10 = 120. And 120 - 112 = 8! This also works if we end up with a total that is a multiple of 10. Since Math.Ceiling(11.0) returns 11, 11 x 10 = 110. 110 - 110 = 0!

So, here's the code to do that:

int checkdigit = (int)Math.Ceiling(total / 10.0d) * 10 - total;
Where total = 112

  1. 112 / 10.0d = 11.2
  2. Ceiling of 11.2 = 12.0
  3. 12 * 10 = 120

  4. 120 - 112 = 8



  1. 110 / 10.0d = 11.0

  2. Ceiling of 11.0 = 11.0

  3. 11 * 10 = 110

  4. 110 - 110 = 0


It Works!

Note, Ceiling returns a decimal, but since we will always have a whole number, we can safely cast this as an int.



But yes... there is a 3rd way.... if you're satisfied with this method so far, that's fine. This was our first solution, and it's kind of cool. BUT after looking at the mod function again, we came up with another solution. It doesn't need any casting as int either, is shorter and perhaps faster, I'm not sure.... here's the formula:



int checkdigit = (10 – total % 10) % 10;

What's going on here? Well

  1. 112 % 10 = 2 (remainder)

  2. 10 - 2 = 8

  3. 8 % 10 = 8



  1. 110 % 10 = 0

  2. 10 - 0 = 10

  3. 10 % 10 = 0


Short, concise, easy to read, and I'd imagine... fast.



Here's the enture C# Code for your perusal:



public static int CheckDigit(string strUpc)
{
// strip all non-numeric characters
string upc = System.Text.RegularExpressions.Regex.Replace(strUpc, @"\D", string.Empty);
int total = 0;

//add the odd position digits
for (int x = 0; x < (upc.Length); x += 2) { total += Convert.ToInt32(upc[x].ToString()); } total = total * 3; //add the even position digits for (int x = 1; x < (upc.Length); x += 2) { total += Convert.ToInt32(upc[x].ToString()); } //calculate the checkdigit return (int)Math.Ceiling(total / 10.0d) * 10 - total; /************************ // could also be written: //AS A condensed IF STATEMENT return (total % 10 == 10) ? 0 : 10 - (total % 10); //That is... if total mod 10 = 10 return 0, else 10 - (total mod 10) //or... using MOD the MOD return (10 – total % 10) % 10 *************************/ }
Just stick this into your utility class or anywhere else in your application, and you're good to go.

5 comments:

Anonymous said...

Thanks for the comment. Yeah, your example is a little more practical than mine.

Believe it or not, c# modulus is one of the top keywords from Google to my blog.

Weird.

Atomiton said...

That is weird. Only google knows what goes. I guess modulus isn't used very much. As for usage. I think the majority think the modulus is only useful in the even odd situation.

Anonymous said...

Interesting example. There is a typo on line 3 which should read:
87 + (1 + 3 + 5 + 7 + 9) = 112 instead of 60 + (1 + 3 + 5 + 7 + 9) = 112.

Atomiton said...

Thanks for that. I'll fix that up.

Unknown said...

My company makes products that have a UPC exemption and are ASIN only and we need to create barcodes for each product to ship to Amazon.

I am using Avery Label Pro to create and print the barcodes. I am using barcode format Code 128. The barcode format options allow you to select "Calculate Check Digit". Should I select that box and have the Avery Label Pro calculate a check digit into the bar code?

I have tried it both ways. When I use the scanner on my phone, it does not read the barcode if there is no check digit. If I include the check digit, my scanner does read the barcode correctly as the ASIN.

My original understanding was to not use the Check Digit, but now we are having trouble.

Thanks in advance for your help!

csharp barcode scanning