CODE Polyglot / 直した不具合

誤差を補正するはずの四捨五入の関数が、大きな金額で .5 未満を切り上げていた

年金の累計 30,672,204.48円 → 30,672,205円(正しくは 30,672,204円)

何が起きていたか

シルギアの60本を超えるツールは、2進数の小数の誤差を補正して四捨五入する、共通の関数(roundHalfUp)を使っていました。丸める前に「値 × 1e-9」をほんの少し足して、375 × 2.3 が 862.4999999999999 になるような誤差を打ち消す関数です。

この足す量が値の大きさに比例していたので、金額が大きいと、0.5 に届いていない値まで切り上げていました。

  • 年金の受給開始のツールで、累計 30,672,204.48円 → 30,672,205円(正しくは 30,672,204円)
  • 同じく、年額 1,812,959.499円 → 1,812,960円(正しくは 1,812,959円)
  • 暗号資産の損益のツールで、所得税 52,000.49995円 → 52,001円(正しくは 52,000円)

3,000万円台では 0.03円を足すので、端数が .47 を超えていれば切り上がります。

どのくらいの入力で起きていたか

お金や量を扱う8本のツールの Math.round をこの関数に置き換えた直後(2026-10-22)、本番のコードを全通り確かめると、2本で置き換える前よりずれが増えていました。

ツール試した入力置き換える前置き換えたあと
暗号資産の損益594,740通り3,208通り13,505通り
年金の受給開始280,960通り10,420通り5,674通り(うち新しく出たずれ 4,486か所)

どうやって見つかったか

CODE Polyglot で見つけた丸めのずれ(米の量・子どもの身長)を直す流れで、シルギア側がお金や量の8本をこの関数に置き換えました。直したあとの本番のコードを、CODE Polyglot で全通り確かめたときに見つかりました。

確かめ方は、本番の app.js をブラウザの代わりに Node でそのまま動かし、画面に出た文字を、同じ式を正確な分数で計算した正解と比べるものです(入力はツールあたり数万〜数百万通り)。

記事: 『誤差を補正する四捨五入』が、3,000万円で1円多く切り上げていた

原因

  • 補正の量(値 × 1e-9)が値の大きさに比例していた。2進数の小数の誤差は有効数字16〜17桁目にしか出ないのに、大きな値では補正が有効数字10桁目まで届いていた

どう直したか

  1. 共通の関数を「有効数字12桁に整えてから丸める」形にした(2026-10-24、0c8e62ca8)。値に何かを足すのをやめたので、補正の量が値の大きさで変わらない。丸めて 0 になったときに「-0」と書かないようにもした。関数を使う64本すべてに入れ、同じ型のずれ(toFixed・Math.ceil など、関数を通っていない箇所)も直した
  2. 年金の「差」は、画面に出した整数どうしを引く形にした。 1,000万円台の累計どうしを引いて 12.5円のような差を出す場所は、丸め方では直らなかったため
  3. 暗号資産の損益は、金額・数量・税金を整数(BigInt の分数)で計算する形にした(2026-10-26、b18c89ab6)。12桁に整える方式でも、平均取得単価が1,000万円台で小数第2位まで出すときや、利益が4,000万円を超える所得税で、二重の丸めが起きたため(10,000,777.6049… が 10,000,777.61円、11,066,460.49996 が 11,066,461円)

丸めの関数で「どこまでの誤差を許すか」を1つに決める方式は、どの幅でもどこかで外れました(3つの案を10本・約263万通りで比べた)。そこで、共通の関数は実用の範囲(有効数字12桁まで)用と割り切り、それを超える精度が要る計算だけを整数でする、という整理になっています。

確かめたこと

CODE Polyglot の検証(tools/shirugear-verify/。本番の app.js を動かし、画面の文字を正確な分数の正解と比べる)で、2026-10-26 時点の本番を確かめました。

ツール試した入力本番
子どもの身長291,402通り一致
米の量8,500通り一致
タクシー料金72,071通り一致
水分量51,612通り一致
モバイルバッテリー247,193通り一致
年金の受給開始227,464通り一致
運動の消費カロリー453,794通り一致
ランニングのペース328,228通り一致
単価比較327,387通り一致
暗号資産の損益619,011通り一致

単価比較は、数量 0.001 のような極端な入力で割高率が10億%を超える場合(有効数字12桁の限界。実際の利用では起きない範囲として据え置き)と、単価がちょうど同じ商品の並び順(決まりがない)を、確かめる入力から外しています。

← 直した不具合の一覧へ