2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

(1.005).toFixed(2) は 1.00 なのに Intl は 1.01 — 小数の丸めを実測

2
Posted at

金額や割合を小数第2位に丸めたとき、(1.005).toFixed(2) が "1.00" になって、1.01 を期待していた人は多いと思います。この記事では、toFixed・Math.round・Intl.NumberFormat の3つで、同じ数を丸めたときの結果を Node.js で実測しました。どれが正しいという話ではなく、3つの結果が一致しない数があるので、使い分けの目印を書きます。

確かめた環境

  • Node.js v24.14.1(2026-10-22)
  • 下の出力は、すべて手元でそのまま実行した結果です
  • ブラウザ(Chrome・Safari)では実行していません。特に Intl.NumberFormat の結果は、環境によって違う可能性があります

Q1. (1.005).toFixed(2) はなぜ "1.00" なのか

console.log((1.005).toFixed(2));              // 1.00
console.log(Math.round(1.005 * 100) / 100);   // 1
console.log(1.005 * 100);                     // 100.49999999999999
console.log(new Intl.NumberFormat('en-US', {
  minimumFractionDigits: 2, maximumFractionDigits: 2,
}).format(1.005));                            // 1.01

1.005 は、コンピューターの中では 1.005 ちょうどではありません。桁を増やして表示すると分かります。

console.log((1.005).toFixed(20)); // 1.00499999999999989342

実際の値は 1.00499999999999989… で、1.005 よりほんの少し小さい数です。だから toFixed(2) は切り下げて 1.00 になります。Math.round(1.005 * 100) も、掛け算の結果が 100.49999999999999 になるので 100 に丸まります。

一方で Intl.NumberFormat は 1.01 を返しました。この3つは、同じ 1.005 を渡しても答えが2通りに分かれます。

Q2. .5 のとき、toFixed と Math.round は一致するか

正の数の .5 では、今回の範囲では一致しました。

console.log([0.5, 1.5, 2.5, 3.5].map(n => n.toFixed(0)));   // [ '1', '2', '3', '4' ]
console.log([0.5, 1.5, 2.5, 3.5].map(Math.round));          // [ 1, 2, 3, 4 ]

偶数に寄せる丸め(2.5 → 2)ではなく、.5 は大きい方へ切り上がっていました。

Q3. マイナスの .5 では結果が分かれる

console.log([-0.5, -1.5, -2.5].map(Math.round));     // [ -0, -1, -2 ]
console.log((-2.5).toFixed(0), (-1.5).toFixed(0));   // -3 -2

Math.round(-2.5) は -2、(-2.5).toFixed(0) は -3 でした。Math.round は .5 をプラス方向(数直線の右)へ、toFixed は、ちょうど半分のとき0から遠い方へ丸めていました。Intl.NumberFormat の -2.5(小数なし)は -3、0.5 は 1 でした。

返金やマイナスの差額を丸めるコードでは、Math.round と toFixed を入れ替えると結果が変わります。

また、Math.round(-0.5) の結果は -0 です。console.log では 0 と表示されるので気づきにくいですが、Object.is(Math.round(-0.4), -0) は true でした。

Q4. toFixed の戻り値は文字列

console.log(typeof (1.5).toFixed(1));        // string
console.log((1.5).toFixed(1) + 1);           // 1.51

足し算に使うと、数ではなく文字がつながって "1.51" になります(小数の足し算そのものがずれる話は、MENTAの別記事で書きました)。計算に戻すときは Number(...) で数に直します。

Q5. 小数第1位や第2位でも、見た目と結果がずれる

console.log((8.345).toFixed(2));     // 8.35
console.log((1.45).toFixed(1));      // 1.4
console.log((10.235).toFixed(2));    // 10.23
console.log((1.45).toFixed(20));     // 1.44999999999999995559

8.345 は 8.35 に上がり、1.45 と 10.235 は下がりました。どれも「5で切り上げ」ではなく、コンピューターが持っている実際の値がちょうどより上か下かで決まっています。toFixed(20) で見ると、8.345 は 8.34500000000000063949(上)、10.235 は 10.23499999999999943157(下)、1.45 は 1.44999999999999995559(下)でした。

Q6. 大きすぎる数は指数表記になる

console.log((1e21).toFixed(2));   // 1e+21

1e21 以上の数では、toFixed は小数点の表示にならず、指数表記の文字列を返します。

使い分けの目印

実測した範囲で、次のように考えると迷いにくいです。

  • 画面に見せるだけ → Intl.NumberFormat(桁区切りもできる。ただし環境差は未確認)
  • 「1.005 → 1.01」を期待する金額計算 → 一般的には、小数の Number ではなく整数(円・銭の単位)で持つ方法があります。この記事では実測していません。銭未満の端数をどう丸めるかは別に決める必要があります
  • 一時しのぎ → Number((x + Number.EPSILON).toFixed(2)) は、1.005 では 1.01 になりました。ただし 2.675 では 2.67 のままでした(toFixed だけでも 2.67)。どの数でも通る方法ではありません
  • マイナスが入るなら、Math.round と toFixed を混ぜない

AIにコードを書かせるときの確認

AIが書いた金額計算に toFixed や Math.round が入っていたら、1.005・2.5・-2.5 の3つを入力して、期待した結果になるか確かめてください。テストが「100円 → 110円」のようなきれいな数だけだと、丸めのずれは見つかりません。


未経験者向けの講座を運営しています

未経験から Next.js + Supabase + Claude Code で Webアプリを公開するまで を、全24セッションで体系化した教材です。Claude Code を学習パートナーにする CLAUDE.md と学習モード(learner / developer)の設計までセットで含みます。

※ 中上級者には易しすぎる内容なので、初心者の知り合いへの紹介や社内研修の参考としてどうぞ。

2
0
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
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?