きっかけ
不動産広告で「80 m²」と書かれていても、住宅情報誌では「24 坪」で書かれる、みたいな状況はザラです。そして畳サイズは地域差があって、同じ「6 畳間」でも江戸間と京間で実面積が違う。賃貸サイトで広さを比較したい時、これを頭の中で換算するのは現実的に無理。
しかも、坪・畝・反・町 の日本伝統単位を調べていくと、「1 坪 = 400 / 121 m²」という正確な分数が出てきました。四捨五入した近似値ではなく、計量法でこの分数が法定値として定義されている。なぜそんな奇妙な数? という話が面白くて、面積単位変換ツールとして 15 単位をひとつに並べました。
作ったもの
面積単位変換 — https://sen.ltd/portfolio/area-units/
15 単位が同時に見えます:
- SI 系: cm² / m² / アール (a) / ヘクタール (ha) / km²
- Imperial: 平方フィート / 平方ヤード / エーカー
- 日本伝統: 坪 / 畳(江戸間 / 京間 / 中京間 / 団地間) / 畝 (せ) / 反 (たん) / 町 (ちょう)
vanilla JS + HTML + CSS、ゼロ依存、ビルドツール不要。node --test で 10 ケース。
1 坪 = 400 / 121 m² の正体
「1 坪 ≈ 3.30578 m²」と書かれることが多いですが、正確な定義は分数です。
tsubo: { toM2: 400 / 121, label: '坪', ja: '坪' },
由来:
- 1 坪 = 6 尺四方 (6 尺 × 6 尺)
- 1 尺 = 10/33 m(1891 年の計量法で定義)
- 1 坪 = (6 × 10/33)² = (60/33)² = (20/11)² = 400/121 m²
「1 尺 = 10/33 m」という定義が鍵。メートル法と尺貫法を 1 対 1 対応させるため、尺を 10/33 m という分数で定義しています。33 分の 10 という奇妙な値は、尺をメートルに換算した歴史的経緯(フランスのメートル原器の長さと日本の曲尺の長さの比)から来ています。
これにより「1 尺 × 33 = 10 m」「10 尺 × 33 = 100 m」みたいに整数関係で繋がるので、土地測量の時に分数が累積しないようになっています。1891 年の立法者がちゃんと計算した跡が、100 年以上経った今もコードの中に残っている。
1 反 = 300 坪 = 120000/121 m²
反・町・畝 も全部「坪の倍数」で定義されているので、分数が綺麗に伝播します:
se: { toM2: 30 * 400 / 121, label: '畝', ja: '畝(せ)' },
tan: { toM2: 300 * 400 / 121, label: '反', ja: '反(たん)' },
cho: { toM2: 3000 * 400 / 121, label: '町', ja: '町(ちょう)' },
- 1 畝 (せ) = 30 坪
- 1 反 = 300 坪 = 10 畝
- 1 町 = 3000 坪 = 100 畝 = 10 反
10 進で倍数になっているのが偶然ではなく計画的で、江戸期の検地のときからこの関係で組まれている。「測量単位を 10 進で設計する」という発想は明治より前からあったと分かります。
畳の 4 種類問題
日本の不動産広告でよく「6 畳」と書かれますが、畳 1 枚のサイズは地域で違います:
jo_edoma: { toM2: 1.5488, label: '畳(江戸間)', ja: '畳(江戸間)' },
jo_kyoma: { toM2: 1.8240, label: '畳(京間)', ja: '畳(京間)' },
jo_chukyo: { toM2: 1.6562, label: '畳(中京間)', ja: '畳(中京間)' },
jo_danchi: { toM2: 1.4455, label: '畳(団地間)', ja: '畳(団地間)' },
一番大きい「京間」と一番小さい「団地間」の差は 約 26%。同じ「6 畳間」と聞いても、京間なら 10.9 m²、団地間なら 8.7 m²。2 m² 以上の差が出ます。
江戸間は 880 × 1760 mm、京間は 955 × 1910 mm。なぜ違うか、というと、関東と関西で家の寸法の取り方が違ったからです:
- 京間(関西・西日本): 畳を先に決めて、それに合わせて家を建てる(畳割り)
- 江戸間(関東・東日本): 家の柱を先に決めて、畳を隙間に合わせる(柱割り)
つまり京間は「畳のサイズ」が固定で、江戸間は「柱と柱の間隔」が固定。建築手法が違うので、結果として畳のサイズが違う。100 年以上前の建築様式の違いが、今の不動産広告にまで残っているのが面白い。
団地間はさらに小さくて、戦後の公団住宅で「小さな部屋に 6 畳と名付けられるように」というコスト最適化の産物。同じ単語で 26% 違う面積を指すのは、単位系としてはわりと異常な状況。
convert / convertAll の 2 関数
変換ロジックは「全単位を m² 経由にする」パターン:
export function convert(value, fromUnit, toUnit) {
if (!Number.isFinite(value)) return { error: 'invalid value' }
const from = UNITS[fromUnit]
const to = UNITS[toUnit]
if (!from || !to) return { error: 'unknown unit' }
const m2 = value * from.toM2
return { value: m2 / to.toM2, unit: toUnit }
}
単一の中間表現 (m²) を経由することで、N × N の変換テーブルを持たずに済みます。15 単位 × 15 単位 = 225 エントリのテーブルじゃなくて、15 個の「m² への換算係数」だけで OK。
UI には 全単位を一度に出す 2 つ目の関数を用意:
export function convertAll(value, fromUnit) {
const from = UNITS[fromUnit]
const m2 = value * from.toM2
const out = {}
for (const [id, def] of Object.entries(UNITS)) {
out[id] = m2 / def.toM2
}
return out
}
「100 m² は何坪?何畳?何エーカー?」を 1 行で 15 通り返す。side-by-side 比較 UI の基盤になります。
テスト
node --test で 10 ケース。特に法定比率の厳密性:
test('1 tsubo equals exactly 400/121 m²', () => {
const r = convert(1, 'tsubo', 'm2')
assert.equal(r.value, 400 / 121)
})
test('1 tan equals 300 tsubo', () => {
const r = convert(1, 'tan', 'tsubo')
assert.equal(r.value, 300)
})
test('1 cho equals 10 tan', () => {
const r = convert(1, 'cho', 'tan')
assert.equal(r.value, 10)
})
test('roundtrip: 100 m² → tsubo → m²', () => {
const a = convert(100, 'm2', 'tsubo').value
const b = convert(a, 'tsubo', 'm2').value
assert.ok(Math.abs(b - 100) < 1e-9)
})
test('京間 > 中京間 > 江戸間 > 団地間', () => {
assert.ok(UNITS.jo_kyoma.toM2 > UNITS.jo_chukyo.toM2)
assert.ok(UNITS.jo_chukyo.toM2 > UNITS.jo_edoma.toM2)
assert.ok(UNITS.jo_edoma.toM2 > UNITS.jo_danchi.toM2)
})
最後の「畳の大小関係」テストは、雑学がコードに残るタイプのテスト。面白いし、データを更新する時の typo 防止にもなる。
おわりに
SEN 合同会社の ポートフォリオシリーズ 100+ の 19 件目です。
- 📦 レポジトリ: https://github.com/sen-ltd/area-units
- 🌐 ライブデモ: https://sen.ltd/portfolio/area-units/
- 🏢 会社: https://sen.ltd/
他に「これも入れて」の単位があれば Issue で。
