どの言語で起きるか
シルギアのツールは JavaScript で書かれています。この不具合が見つかったケースを、CODE Polyglot で10言語の「よくある書き方」で回した結果です(言語を返した答えごとにまとめ、赤い値は実際に返した間違った答え)。 JavaScript は 3件中3件で外れました。
| ケース | 正解 | よくある書き方の答え(10言語) |
|---|---|---|
| c010税込1100円から割戻し・10%・切り捨て |
|
|
| c011税込1080円から割戻し・8%・切り捨て |
|
|
| c012税込110円から割戻し・10%・切り捨て |
|
|
何が起きていたか
請求書の消費税を計算するツールで、税込の金額から税抜を求める(割戻し)と、税抜が1円少なくなることがありました。
- 税込1,100円・10%・切り捨ての税抜は、1,100 ÷ 1.1 = 1,000円ちょうどです。ツールは 999円(税額101円)と表示していました
- 税込1,080円・8%(正しくは1,000円)も 999円、税込110円・10%(正しくは100円)も 99円 でした
ずれるのは、割り切れて税抜がちょうど整数になる金額です。割り切れない金額や、税抜から税額を求める計算では起きていませんでした。
どのくらいの入力で起きていたか
直す前の式を Node でそのまま動かし、分数で計算した正確な値と比べました。範囲は、金額 1〜100,000円(1円刻み)× 税率 8%・10% × 端数処理3種(切り捨て・四捨五入・切り上げ)です。
| 計算 | 調べた数 | ずれた数 |
|---|---|---|
| 税込から税抜(割戻し)・10%・切り捨て | 100,000通り | 4,174通り |
| 税込から税抜(割戻し)・8%・切り捨て | 100,000通り | 818通り |
| 税込から税抜(割戻し)・四捨五入・切り上げ | 400,000通り | 0通り |
| 税抜から税額 | 600,000通り | 0通り |
ずれたのはすべて、割り切れる金額でした。10%で割り切れる税込金額(11の倍数)は100,000通り中9,090通りあり、そのうち4,174通りで1円少なくなっていました。8%(27の倍数)は3,703通り中818通りです。
どうやって見つかったか
CODE Polyglot の最初の仕様 請求書の消費税と端数処理 で、正解表の c010〜c012(税込1,100円・1,080円・110円からの割戻し)を作りました(2026-10-09)。PHP と Python のよくある書き方が、3件とも1円少ない答えを返しました。
元のツールの式(Math.floor(inclusive / (1 + rate)))を Node で動かすと、同じく 999円 になりました。その日のうちに、シルギア側で直しました。
原因
0.1 や 1.1 は、2進数の小数では正確に表せません。そのため 1100 / 1.1 は 1000 ではなく 999.9999999999999 になり、切り捨てると 999 になります(解説)。
どう直したか
計算の部分を calc.js に分け、金額と税率を十進の文字列のまま分数(BigInt の分子と分母)にしました。割り算は整数の商で端数処理するので、2進数の小数を一度も通りません。
- var rate = pct / 100; - exclusive = applyRounding(inclusive / (1 + rate), mode); + return { n: BigInt(intPart + fracPart), d: 10n ** BigInt(fracPart.length) }; + const rate = divFrac(pct, { n: 100n, d: 1n }); + const onePlusRate = addFrac({ n: 1n, d: 1n }, rate); + const exclusiveInt = roundToYen(divFrac(inclusive, onePlusRate), mode); // BigInt 円
+ if (mode === "ceil") return (n + d - 1n) / d; + if (mode === "round") return (2n * n + d) / (2n * d); // floor(n/d + 1/2) + return n / d; // floor(既定: 切り捨て)
任意の税率(7.5% など)も、十進のまま計算します。
確かめたこと
- シルギア側のテスト20件が、すべて通過(CODE Polyglot から実行して確認・2026-10-09)
- 正解表19件のうち、元のツールが受け付ける範囲(負の金額と 2^53 を超える金額を除く)の14件を新しい
calc.jsに通し、14件中14件が一致 - 修正後の本番のページをヘッドレス Chrome で操作し、税込1,100円・1,080円・110円の割戻しが 1,000円・1,000円・100円 と表示されることを確認(2026-10-09)