JANの末尾1桁はどう決まるか — 重み1と3で検算する

商品パッケージの裏にある13桁のバーコード番号。あの末尾1桁は入力するものではなく、前の12桁から計算で決まる

だから「12桁までは合っているのに末尾が違う」という状態は、原理的に打ち間違いが検出されたということを意味する。逆に言えば、末尾を自分で決められると思っていると、番号を扱うコードは検算の機会をまるごと捨てることになる。

計算式は驚くほど短い。この記事では EAN-13/JAN の検査数字がどう決まるかを、実在商品の JAN コードで検算しながら確かめる。UPC-A と Luhn との違いも並べて押さえる。手で試すなら チェックディジット計算ツールがそのまま使える。

EAN-13/JAN の計算

手順は3つだけである。

  1. 左から12桁について、奇数番目(1,3,5…)に重み1、偶数番目(2,4,6…)に重み3を掛けて合計する
  2. 合計を10で割った余りを求める
  3. 10からその余りを引き、さらに10で割った余りを取る。それが検査数字

3つ目で二重に10を取っているのは、余りが0のときに検査数字を10ではなく0にするためである。ここを 10 - (sum % 10) だけで済ませると、余り0のときに 10 という2桁が出てしまう。よくある実装バグがこれだ。

JavaScript で書くと5行に収まる。

1
2
3
4
5
function ean13CheckDigit(body12) {
let sum = 0;
for (let i = 0; i < 12; i++) sum += Number(body12[i]) * (i % 2 === 0 ? 1 : 3);
return (10 - (sum % 10)) % 10;
}

i は0始まりなので、i % 2 === 0 が「1桁目・3桁目…」つまり重み1の側にあたる。ここで0始まりと1始まりを取り違えると、重み1と3が入れ替わって別の数字が出る。しかも一見それらしい1桁が返るので、テストが無いと気づけない。

実在の JAN で検算する

抽象的な例より、実際に売っている商品の番号で確かめたほうが早い。エースコックが2026年8月3日に発売した「だしの旨みで減塩」シリーズ3品の JAN コードを使う(麺類のセール情報にまとめた商品である)。

商品 JAN
だしの旨みで減塩 中華そば 4901071410513
だしの旨みで減塩 小海老天そば 4901071410490
だしの旨みで減塩 鶏炊きうどん 4901071245399

1つ目を手で追ってみる。本体は 490107141051 の12桁だ。

1
2
3
4
桁位置:  1 2 3 4 5 6 7 8 9 10 11 12
数字: 4 9 0 1 0 7 1 4 1 0 5 1
重み: 1 3 1 3 1 3 1 3 1 3 1 3
積: 4 27 0 3 0 21 1 12 1 0 5 3

合計は 4+27+0+3+0+21+1+12+1+0+5+3 = 7777 % 10 = 7(10 - 7) % 10 = 3

実際の JAN の末尾は 3。一致する。

先頭の 49 は日本の国コードで、4549 が日本に割り当てられている。国コードから始まって企業コード、商品コードと続き、最後にこの検査数字が付く。

実際に試す

3品ぶんをまとめて検算する。依存パッケージは無く、Node.js があれば動く。

前提: Node.js 18 以上(node -e が使えれば版は問わない)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// checkdigit-verify.js
function ean13CheckDigit(body12) {
let sum = 0;
for (let i = 0; i < 12; i++) sum += Number(body12[i]) * (i % 2 === 0 ? 1 : 3);
return (10 - (sum % 10)) % 10;
}

function verify(jan) {
const digits = String(jan).replace(/\D/g, "");
if (digits.length !== 13) return { jan, ok: false, reason: `13桁ではありません(${digits.length}桁)` };
const body = digits.slice(0, 12);
const actual = Number(digits.slice(-1));
const expected = ean13CheckDigit(body);
return { jan: digits, expected, actual, ok: expected === actual };
}

const samples = [
["だしの旨みで減塩 中華そば", "4901071410513"],
["だしの旨みで減塩 小海老天そば", "4901071410490"],
["だしの旨みで減塩 鶏炊きうどん", "4901071245399"],
["わざと1桁変えたもの", "4901071410514"],
];

for (const [name, jan] of samples) {
const r = verify(jan);
const mark = r.ok ? "OK" : "NG";
const detail = r.reason ? r.reason : `期待 ${r.expected} / 実際 ${r.actual}`;
console.log(`${mark} ${jan} ${detail} ${name}`);
}

実行する。

1
node checkdigit-verify.js

出力は次のとおりになる。

1
2
3
4
OK  4901071410513  期待 3 / 実際 3  だしの旨みで減塩 中華そば
OK 4901071410490 期待 0 / 実際 0 だしの旨みで減塩 小海老天そば
OK 4901071245399 期待 9 / 実際 9 だしの旨みで減塩 鶏炊きうどん
NG 4901071410514 期待 3 / 実際 4 わざと1桁変えたもの

4行目が検査数字の役割そのものである。末尾を1つずらしただけで検出される。

ただし万能ではない。 この方式が確実に見つけられるのは1桁の打ち間違いで、隣り合う2桁の入れ替えは取りこぼすことがある。重み1と3の差が2なので、入れ替えによる合計の変化が10の倍数になる組み合わせでは、検査数字が変わらない。実際に 4901071410513 の途中の隣接2桁を入れ替えて試すと、検出される組と検出されない組の両方が出る。「検査数字が通った=番号が正しい」ではない、という点は押さえておく必要がある。

UPC-A と Luhn は式が違う

同じ「重みを掛けて10の補数」という枠でも、方式ごとに中身が違う。混同すると、正しい番号を不正と判定してしまう。

UPC-A(12桁)は重みが逆である。 奇数番目に3、偶数番目に1を掛ける。EAN-13 と同じ関数を使い回すと結果がずれる。

1
2
3
4
5
function upcaCheckDigit(body11) {
let sum = 0;
for (let i = 0; i < 11; i++) sum += Number(body11[i]) * (i % 2 === 0 ? 3 : 1);
return (10 - (sum % 10)) % 10;
}

ISBN-13 は EAN-13 と同じ式である。 書籍用の 978 / 979 で始まる EAN-13 なので、計算は共通で構わない。ISBN-10 は11の剰余を使い末尾に X が出ることがある別方式だが、これは13桁への移行で使われる場面が減っている。

Luhn(クレジットカード等)は倍にして桁を足す。 右から見て1つおきに2倍し、2倍した結果が9を超えたら9を引く。重みを掛けるだけの EAN 系とは手順が違う。

1
2
3
4
5
6
7
8
9
function luhnCheckDigit(body) {
let sum = 0, dbl = true;
for (let i = body.length - 1; i >= 0; i--) {
let n = Number(body[i]);
if (dbl) { n *= 2; if (n > 9) n -= 9; }
sum += n; dbl = !dbl;
}
return (10 - (sum % 10)) % 10;
}

Luhn だけ本体の桁数が固定でない点も違う。EAN-13 は本体12桁、UPC-A は本体11桁と決まっているが、Luhn はカードブランドによって全体の桁数が変わるため、桁数チェックを式に組み込めない。

4方式を切り替えながら手で確かめたいときは、チェックディジット計算ツールが EAN-13/JAN・UPC-A・ISBN-13・Luhn をタブで切り替えられる。計算はすべてブラウザ内で完結し、入力した番号は送信されない。カード番号のような値を扱うときはこの性質が効く。

どこで使うか

実務でいちばん効くのは、外部から来た番号を受け取る境界である。フォーム入力、CSV 取り込み、OCR やバーコード読み取りの結果。ここで検算しておくと、誤った番号がデータベースに入る前に止まる。

逆に、自分で採番した番号を保存するときに毎回検算しても得るものは少ない。検査数字は転記の誤りを見つけるためのもので、値そのものの正しさを保証するものではないからだ。


JANの末尾1桁はどう決まるか — 重み1と3で検算する
https://blog.hashito.biz/2026/08/22/ean13-check-digit-weight-1-3-jan-verify/
著者
hashito
作成日
2026年8月22日
著作権