EAN-13 と UPC-A で重み 1/3 が入れ替わる理由
バーコードの検査数字(チェックディジット)を自分で計算しようとすると、必ず一度は同じところで詰まる。EAN-13 と UPC-A で、重みの 1 と 3 が入れ替わっているからだ。
どちらもモジュラス10で、どちらも「1桁おきに3倍する」と説明される。それなのに実装をコピーして流用すると、片方だけ答えが合わない。
この記事では、ハシトシステムのチェックディジット計算ツールが実際に使っている実装をそのまま追いながら、なぜ入れ替わるのか、どこを間違えやすいのかを整理する。最後に Node.js で既知の番号を使って検算するスクリプトを置く。
実装は3行しかない
このツールが持っている計算部分は、方式ごとに1関数ずつ、合計3つである。
1 | |
ブラウザ内で完結する作りなので、入力した番号がどこかへ送られることはない。計算の実体はこれで全部である。
上の2つを並べると、違いが1か所だけなのが分かる。
weighted13…i%2===0のとき 1、それ以外 3weightedUPC…i%2===0のとき 3、それ以外 1
ループの範囲も違う(12 と 11)。EAN-13 は本体12桁、UPC-A は本体11桁だからだ。
なぜ逆になるのか
規格の定義はどちらも同じ言い方をしている。「右端(検査数字)から数えて奇数番目に 3 を掛ける」である。
つまり基準が右端なのだ。左から i=0 で回す実装に直すとき、本体の桁数が偶数か奇数かで、左端が偶数番目に当たるか奇数番目に当たるかが変わる。
- EAN-13 の本体は 12桁(偶数)。右から数えた重み
3,1,3,1,...を左から並べ直すと1,3,1,3,...になる - UPC-A の本体は 11桁(奇数)。同じことをすると
3,1,3,1,...のまま
規格が違うのではない。桁数の偶奇が違うだけである。 ここを「EAN と UPC は別ルール」と覚えると、他の桁数の方式に出会ったときにまた間違える。覚えるなら「右端基準」のほうを覚える。
ISBN-13 が weighted13 を共有しているのも同じ理由だ。ISBN-13 は EAN-13 の一種で、本体12桁だから式が完全に一致する。ツール側も SPEC の定義で isbn13 に weighted13 を割り当てているだけで、別実装は持っていない。
Luhn だけ考え方が違う
クレジットカード番号などに使う Luhn は、上の2つとは別の系統である。
1 | |
違いは2つある。
1つ目。掛けるのは 3 ではなく 2 である。 そして 2倍して10以上になったら 9 を引く。これは「10の位と1の位を足す」と同じ意味だ。たとえば 8×2=16 は 1+6=7 で、16-9=7 と一致する。
2つ目。右端から始める。 for が d.length-1 から下がっているのはそのためで、dbl=true で始まる。つまり本体の右端が必ず2倍される。だから本体の桁数が何桁でもこの実装は正しく動く。EAN や UPC のように桁数を固定していないのはこの性質による。
ツールの SPEC でも luhn だけ body: null になっていて、桁数の検査をしていない。
実際に試す
Node.js 20 以降があれば依存は要らない。cd.js として保存する。既知の正しい番号で検算する。
1 | |
実行する。
1 | |
出力はこうなる。
1 | |
入れ替わりを体感するなら、ここで1文字だけ変えてほしい。 weighted13 の (i%2===0?1:3) を (i%2===0?3:1) に変えて実行すると、ISBN-13 の2件だけが NG になる。UPC-A と Luhn は影響を受けない。この差の出方が、そのまま「桁数の偶奇で左右が入れ替わる」ことの現れである。
検算に使った番号の性質も書いておく。4111111111111111 は決済処理のテストで広く使われる番号で、実在のカードではない。79927398713 は Luhn の説明でよく引かれる例である。ISBN-13 の2件は書籍の識別子で、末尾の 5 と 0 が規格どおりの検査数字になっている。
使い分けの目安
自分で書くか、ツールを叩くかの判断は単純だ。
バッチで大量に処理する、あるいは入力検証に組み込むなら自分で書く。 上のとおり実装は3行で、外部ライブラリを入れる理由がない。桁数の固定だけ気をつければよい。
1件だけ確かめたい、あるいは自分の実装が合っているか照合したいなら、チェックディジット計算ツールのほうが速い。EAN-13/JAN・UPC-A・ISBN-13・Luhn を切り替えられて、本体だけを入れれば検査数字を出し、検査数字まで入れれば妥当かどうかを判定する。ブラウザ内で完結するので、番号が送信されることはない。
実務で効くのは後者の「照合」のほうだと思う。自分の実装を信じきってバッチを流すより、既知の1件で先に突き合わせておくほうが、あとで全件やり直す事故を防げる。
まとめ
- EAN-13 と UPC-A の重みが逆に見えるのは、規格が違うからではなく本体の桁数の偶奇が違うから。定義はどちらも「右端から数えて奇数番目に3を掛ける」
- ISBN-13 は EAN-13 と本体12桁で一致するので、実装を共有できる
- Luhn は 2倍して9を引く方式で、右端から始めるため桁数を固定しなくてよい
- 実装は3行。ただし自分の実装は、既知の番号で必ず1件照合してから流す
本記事のコードは、ハシトシステムのチェックディジット計算ツールの実装に基づいている。出力例は Node.js 20.17.0 で実際に実行した結果である。