ISBN-10とISBN-13のチェックディジット変換と見逃す入れ替え

書籍のISBNは、10桁の旧規格と13桁の現行規格で最後の1桁(チェックディジット)の計算法が違う。10桁から13桁への変換は「先頭に978を付けて末尾を計算し直す」だけだが、計算式を取り違えると末尾が合わない。ここでは両方を Node.js で実装し、変換結果と、隣り合う数字の入れ替えを検出できるかどうかを実測した。

手計算の答え合わせには ISBN変換(10桁⇔13桁)(ハシトシステム) が使える。10桁と13桁を相互変換し、チェックディジットが正しいかを判定する。以下はそのロジックを手元で再現するコードである。

2つの規格の計算式

  • ISBN-10: 先頭9桁に重み10, 9, 8, …, 2 を掛けて合計し、(11 - 合計 % 11) % 11 を末尾にする。結果が10のときは X と書く。
  • ISBN-13: 先頭12桁に重み 1, 3, 1, 3, … を交互に掛けて合計し、(10 - 合計 % 10) % 10 を末尾にする。
  • 10桁から13桁への変換は、先頭9桁の前に 978 を付けた12桁から ISBN-13 の末尾を計算し直す。ISBN-10 の末尾は引き継がない。
  • 13桁から10桁への逆変換は 978 始まりに限られる。979 始まりには対応する ISBN-10 がない。

実際に試す

isbn.mjs として保存する。Node.js v24.11.1、Windows PowerShell 5.1 で実行した。

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
30
31
32
33
34
35
36
37
// isbn.mjs : ISBN-10 と ISBN-13 のチェックディジットを計算し、相互変換する
const d10 = (s) => s.replace(/-/g, '');

function check10(body9) {
let sum = 0;
for (let i = 0; i < 9; i++) sum += Number(body9[i]) * (10 - i);
const c = (11 - (sum % 11)) % 11;
return { sum, c: c === 10 ? 'X' : String(c) };
}
function check13(body12) {
let sum = 0;
for (let i = 0; i < 12; i++) sum += Number(body12[i]) * (i % 2 === 0 ? 1 : 3);
return { sum, c: String((10 - (sum % 10)) % 10) };
}
const to13 = (isbn10) => { const b = '978' + d10(isbn10).slice(0, 9); return b + check13(b).c; };
const to10 = (isbn13) => {
const s = d10(isbn13);
if (!s.startsWith('978')) throw new Error('979始まりは ISBN-10 に変換できない');
const b = s.slice(3, 12); return b + check10(b).c;
};

console.log('ISBN-10 本体 030640615 :', check10('030640615'));
console.log('ISBN-13 本体 978030640615 :', check13('978030640615'));
console.log('0-306-40615-2 -> 13 :', to13('0-306-40615-2'));
console.log('9780306406157 -> 10 :', to10('9780306406157'));
console.log('X になる例 080442957 :', check10('080442957'), '->', to13('080442957X'));
try { to10('9791234567896'); } catch (e) { console.log('979:', e.message); }
// 隣り合う数字の入れ替えを検出できるか
const swap = (s, i) => s.slice(0, i) + s[i + 1] + s[i] + s.slice(i + 2);
const ok13 = (s) => check13(s.slice(0, 12)).c === s[12];
const ok10 = (s) => check10(s.slice(0, 9)).c === s[9];
let miss13 = [], miss10 = [];
const a13 = '9780306406157', a10 = '0306406152';
for (let i = 0; i < 12; i++) { const t = swap(a13, i); if (t !== a13 && ok13(t)) miss13.push(i); }
for (let i = 0; i < 9; i++) { const t = swap(a10, i); if (t !== a10 && ok10(t)) miss10.push(i); }
console.log('ISBN-13 で見逃す入れ替え位置:', miss13, ' ISBN-10:', miss10);
console.log('Node', process.version);
1
node isbn.mjs

出力:

1
2
3
4
5
6
7
8
ISBN-10 本体 030640615 : { sum: 130, c: '2' }
ISBN-13 本体 978030640615 : { sum: 93, c: '7' }
0-306-40615-2 -> 13 : 9780306406157
9780306406157 -> 10 : 0306406152
X になる例 080442957 : { sum: 199, c: 'X' } -> 9780804429573
979: 979始まりは ISBN-10 に変換できない
ISBN-13 で見逃す入れ替え位置: [ 9 ] ISBN-10: []
Node v24.11.1

読み取れること

  • 030640615 の合計は130で、130 % 11 = 9、11 - 9 = 2 だから末尾は 2。ISBN-10 は 0-306-40615-2 になる。
  • 同じ本体を 978 付きの12桁にすると合計は93、93 % 10 = 3、10 - 3 = 7 で末尾は 7。ISBN-13 は 9780306406157 になる。10桁の末尾 2 は13桁には残らない。
  • 080442957 は合計が199、199 % 11 = 1、11 - 1 = 10 なので末尾が X になる。13桁へ変換すると X は消えて 9780804429573 になる。
  • 979 始まりの 9791234567896 は10桁へ変換できず、例外になる。

入れ替えエラーの検出力

チェックディジットは、タイプミスを見つけるための仕組みである。隣り合う2つの数字を入れ替えたとき、検算に通ってしまう位置を総当たりで調べた。

  • ISBN-10 は、0306406152 の隣接入れ替えを全9箇所で検出した。重みが10から2まで1ずつ違い、mod 11 が素数だからである。
  • ISBN-13 は、9780306406157 の入れ替え12箇所のうち1箇所(添字9、6 と 1 の入れ替え)を見逃した。差が5の数字を重み1と3の位置で入れ替えると、合計が (3-1)×5 = 10 だけ動き、mod 10 では変わらないためである。
  • 隣り合う数字の差が5のときは、ISBN-13 では入れ替えても検算に通る場合がある。今回の例は1つの ISBN で調べた結果であり、すべての ISBN で同じ位置とは限らない。

つまり、ISBN-13 のチェックを通っても、入力が正しいとは限らない。確実に確かめたいときは書名や出版社の検索結果も見る。


ISBN-10とISBN-13のチェックディジット変換と見逃す入れ替え
https://blog.hashito.biz/2026/10/21/isbn10-isbn13-check-digit-conversion-swap-miss-js/
著者
hashito
作成日
2026年10月21日
著作権