0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

1000 は「千」なのに 10000 は「一万」。漢数字で「一」を省ける位と省けない位

0
Posted at

背景

自分のサイトに、数字と漢数字を相互変換するツールを置いています。
年賀状やのし袋の金額を縦書きにするときに、毎回「一万円」と書くのか「万円」なのか迷うので作りました。
作ってみて、1 を省ける位と省けない位があることを、ちゃんと意識していなかったことに気づきました。
その備忘録です。

漢数字の位取り表記は、十・百・千で1桁ずつ、万・億・兆で4桁ずつ位を上げる書き方だ。
12345678 なら「千二百三十四万五千六百七十八」になる。

結論

十・百・千の前の「一」は省き、万・億・兆の前の「一」は省きません。

  • 1000 → 千
  • 10000 → 一万
  • 10000000 → 千万
  • 100000000 → 一億

「千円」とは言っても「万円」とは言わないのと同じです。
千万の「千」は、万の前に付く数字のほうなので、一は付けません。

数字から漢数字へ

ツールでは下記のように、4桁ずつ区切って変換しています。
numStr は画面で入力した数字の文字列(カンマや空白は取り除いたもの)です。
16桁(兆の位)までを扱います。

function numToKanji(numStr) {
  let s = String(numStr).replace(/[,\s]/g, "").replace(/^0+(?=\d)/, "");
  if (!/^\d+$/.test(s) || s.length > 16) return null;
  if (s === "0") return "零";
  const digits = ["", "一", "二", "三", "四", "五", "六", "七", "八", "九"];
  const units = ["", "十", "百", "千"];
  const groups = ["", "万", "億", "兆"];
  let out = "";
  let gi = 0;
  while (s.length) {
    const chunk = s.slice(-4); // 下から4桁
    s = s.slice(0, -4);
    let part = "";
    for (let i = 0; i < chunk.length; i++) {
      const d = +chunk[chunk.length - 1 - i];
      if (!d) continue; // 0 の位は書かない
      const dg = d === 1 && i > 0 ? "" : digits[d]; // 十百千の前の一を省く
      part = dg + units[i] + part;
    }
    if (part) out = part + groups[gi] + out; // 区切りが全部0なら万なども書かない
    gi++;
  }
  return out;
}

ポイントは、一を省く判定が区切りの中(i > 0、つまり十百千の位)だけで行われることです。
区切りの単位の「万」の前には、区切りの中身が「一」でもそのまま付きます。

いくつか入れてみると、下記になりました(Node.js v24.11.1)。

1        -> 一
10       -> 十
1000     -> 千
1001     -> 千一
10000    -> 一万
11000    -> 一万千
110000   -> 十一万
10000000 -> 千万
2026     -> 二千二十六

10000 が「一万」、10000000 が「千万」になっているのがわかると思います。
11000 は「一万千」で、「一万一千」とは書いていません。
小切手や契約書のように、あとから書き足されると困る場面では「一千」と書くこともありますが、このツールは日常の表記に合わせました(改ざんを防ぎたいなら、壱・弐・拾などの大字のほうを使う前提です)。

漢数字から数字へ

逆向きは、省かれた一も、付いている一も、どちらも読めないといけません。
人が書く漢数字には「一千万」も「千万」もあるし、年号なら「二〇二六」のような数字並びもあるからです。

下記は、ツールの読み取り部分を簡略化したものです。
str は入力欄の文字列で、桁が大きくなっても丸められないように BigInt で足し上げています。

const KDIG = { 〇: 0, 零: 0, 一: 1, 二: 2, 三: 3, 四: 4, 五: 5, 六: 6, 七: 7, 八: 8, 九: 9 };
const KSMALL = { 十: 10n, 百: 100n, 千: 1000n };
const KBIG = { 万: 10000n, 億: 100000000n, 兆: 1000000000000n };

function kanjiToNum(str) {
  let total = 0n, section = 0n, num = 0n, hasNum = false;
  for (const ch of str.replace(/[\s,]/g, "")) {
    if (ch >= "0" && ch <= "9") { num = num * 10n + BigInt(ch); hasNum = true; }
    else if (ch in KDIG) { num = num * 10n + BigInt(KDIG[ch]); hasNum = true; }
    else if (ch in KSMALL) { section += (hasNum ? num : 1n) * KSMALL[ch]; num = 0n; hasNum = false; }
    else if (ch in KBIG) {
      const head = section + num || 1n; // 「万」だけなら一万
      total += head * KBIG[ch];
      section = 0n; num = 0n; hasNum = false;
    } else return null;
  }
  return (total + section + num).toString();
}

section は万未満の区切りの合計、num は直前に読んだ数字だ。
十百千が来たときに、直前に数字が無ければ 1 として掛けるので、「千」も「一千」も 1000 になる。
数字が続いたときは num * 10 + 数字 で桁を上げるので、「二〇二六」は 2026 になる。

同じように入れてみると、下記になりました。

千万         -> 10000000
一千万       -> 10000000
万           -> 10000
二〇二六     -> 2026
二千二十六   -> 2026
3万5千       -> 35000
一〇〇万     -> 1000000

省かれていても付いていても、同じ数になっています。
「3万5千」のように算用数字が混ざっていても読めるので、ニュースの見出しのような表記もそのまま貼れます。

作ったツールはここに置いています。
https://hashitosystem.com/tools/kansuji/

漢数字にするときは、十百千の前の一は落とし、万億兆の前の一は必ず書く。
戻すときは、一が省かれた形も数字並びの形も同じ数として読む、と決めておけば迷いません。


本記事はAI補助で執筆した、個人開発の紹介記事です。

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?