Version 3 of Check digits

Updated 2005-03-18 06:48:12 by suchenwi

if 0 {Richard Suchenwirth 2002-12-15 - Unique item identifiers may at times get pretty lengthy, especially if numeric. For instance, an EAN code (European Article Number) as found on many food and non-food articles, is 13 digits long. In the typical scanning of barcodes, or occasional typing by hand, errors may occur that can be detected by requesting a certain property from the code - most often that a check sum computed by some rules, modulo a number, gives zero. Albrecht Beutelspacher's Pasta all'infinito (München: dtv 2002) taught me some rules, amid much other reading pleasure (highly recommended if you're interested in Math and/or Italy), so here's my Tcl implementations which return 1 if the input code is valid, else 0.

Digits in the EAN code enter the checksum weighted alternatingly by 1 or 3. Let's take for example the EAN of that book:

 EAN:    9  7 8 3 4 2 3 3 3 0 6  9 5
 weight: 1  3 1 3 1 3 1 3 1 3 1  3 1
 gives:  9+21+8+9+4+6+3+9+3+0+6+27+5 = 110, 110 % 10 = 0 -> ok

}

 proc EANvalid ean {
    regsub -all {[^0-9]} $ean "" ean ;# remove all non-digits
    set weight 1
    set sum 0
    foreach digit [split $ean ""] {
        set sum    [expr {$sum + $digit * $weight}]
        set weight [expr {$weight == 1? 3: 1}]
    }
    expr {($sum % 10) == 0}
 }

if 0 {This procedure detects all 1-digit errors, but may fail on 10% of digit swaps (e.g. if 3 8 was typed instead of 8 3). A more robust scheme is used in ISBN (International Standard Book Numbers), ten-digit sequences consisting of the following fields:

 country     publisher  number checkdigit, e.g.
 3 (Germany) 423 (dtv)  33069  4

Weights in this scheme decrease from 10 to 1, and the special twist is that the check digit is "undecimal", meaning both "non-decimal" (because besides the digits 0-9, it may be X) and "base 11" (Latin undecem, X standing for 10). For example, an elderly book that I also really like has the ISBN

 ISBN:    3 - 5  4 0 - 1 0  3  5 2 - X
 weight: 10   9  8 7   6 5  4  3 2   1
 gives:  30+ 45+32+0+  6+0+12+15+4+ 10 = 154, 154%11 = 0 -> ok
 }
 proc ISBNvalid isbn {
    regsub -all {[^0-9X]} $isbn "" isbn
    set weight 10
    set sum 0
    foreach digit [split $isbn ""] {
        if {$digit=="X"} {set digit 10}
        set sum [expr {$sum + $digit * $weight}]
        incr weight -1
    }
    expr {($sum % 11) == 0}    
 }

if 0 {Note also that modern books have a bar-coded EAN which, after the prefix 978, contain the ISBN minus its check digit (EAN is decimal, so X might be a problem), and adding the EAN check digit, so the Beutelspacher book has the EAN

 978   3 423 33069   5
 ISBN: 3-423-33069-4

See also UIC vehicle number validator and Validating Credit Card Check Digits


Category Concept | Arts and crafts of Tcl-Tk programming }

Comment: The weight sequence above is in the wrong order.

         It should be 1 2 3 4 5 6 7 8 9 10
         Goran
         [email protected]

RS: I just re-checked the Beutelsbacher book - the weights from left to right are indeed given there as descending 10 9 .. 2 1. However, I tested several books, and both sequences seem to work.. Funny.