どの言語で起きるか
シルギアのツールは JavaScript で書かれています。この不具合が見つかったケースを、CODE Polyglot で10言語の「よくある書き方」で回した結果です(言語を返した答えごとにまとめ、赤い値は実際に返した間違った答え)。 JavaScript は 5件中5件で外れました。
| ケース | 正解 | よくある書き方の答え(10言語) |
|---|---|---|
| c002袋数・割り切れる(0.1m³ の袋) |
|
|
| c003袋数・割り切れる(0.05m³ の袋) |
|
|
| c004袋数・割り切れる(10L の袋) |
|
|
| c006袋数・25L の袋 |
|
|
| c009体積の四捨五入・0.5 ちょうど |
|
|
何が起きていたか
面積と厚さからコンクリートの必要量と袋数を出すツールで、ちょうど割り切れる袋数が1袋多くなっていました。
- 面積 6.5m²・厚さ 3cm・1袋 0.013m³(初期値): 体積は 0.195m³ で、0.195 ÷ 0.013 = 15袋ちょうどです。ツールは 16袋 と表示していました
- 面積 3m²・厚さ 10cm・1袋 0.1m³ も、0.3 ÷ 0.1 = 3袋のところを 4袋 と表示していました
体積の端数が 0.0005m³ ちょうどになる入力では、体積とリットルが小さい方に丸められることもありました(面積 1.5m²・厚さ 7.5cm の 0.1125m³ を 0.112m³・112L、正しくは 0.113m³・113L)。
どのくらいの入力で起きていたか
袋数は、面積 1〜30m²(0.5m²刻み・59通り)× 厚さ 3・5・7・8・10・12・15・20cm(8通り)の472通りを、1袋の量5種類で数えました。
| 1袋の量 | 1袋多い |
|---|---|
| 0.01m³(10L) | 472通り中56通り |
| 0.013m³(初期値) | 472通り中23通り |
| 0.025m³ | 472通り中25通り |
| 0.05m³ | 472通り中21通り |
| 0.1m³ | 472通り中16通り |
| 合計 | 2,360通り中141通り |
袋数が少なくなる向きのずれは、2,360通り中0通りでした。
体積とリットルは、面積 0.1〜30.0m²(0.1m²刻み)× 厚さ 1.0〜30.0cm(0.5cm刻み)の 17,700通り で数えました。体積(m³・小数第3位まで)とリットルが、どちらも 0.001m³・1L 少なく表示されたのは、17,700通り中712通りです。
どうやって見つかったか
CODE Polyglot の仕様 コンクリート・生コンの必要量 を作るとき、元のツールと同じ式を4言語で書いて、正確な値と突き合わせました(2026-10-15)。正解表16件のうち、ちょうど割り切れる袋数の c002・c003・c004・c006 を、4言語とも1袋多く答えました。体積の四捨五入の c009 なども外しました。
同じ日、シルギア側の品質点検では、このツールは「不具合なし」と判定されていました。袋数の式 ceil(体積 ÷ 1袋の量) そのものは正しく、早見表の値も境目に乗らなかったためです。CODE Polyglot から反例(6.5m²・厚さ 3cm → 16袋)と、上の472通りで数えたずれの件数を示し、その日のうちに直りました。
原因
- 0.195 や 0.013 は2進数の小数では正確に表せません。0.195 ÷ 0.013 は 15.000000000000002 になり、切り上げで16になります。袋数は足りないと困るので切り上げで出しますが、切り上げはこのわずかな誤差を拾います(解説)
- 1.5 × (7.5 ÷ 100) は 0.1125 ではなく 0.11249999999999999 になり、四捨五入で 0.112 に切り下がります(解説)
どう直したか
計算の部分を calc.js に分けました。入力を十進の文字列のまま整数の分子・分母(BigInt)にして計算し、袋数の切り上げと、体積・リットルの四捨五入を整数で行います。
- var volume = area * (thickCm / 100); // m³ - var bags = Math.ceil(volume / bagVol); - var r = Math.round(n * 1000) / 1000; + var v = div(mul(a, t), int(100)); // 体積 m³ + bags: bag.n > 0n ? Number(ceil(div(v, bag))) : null, + var n = halfUp(mul(x, int(1000)));
+ var halfUp = function (x) { return (2n * x.n + x.d) / (2n * x.d); }; // x ≥ 0 + var ceil = function (x) { return (x.n + x.d - 1n) / x.d; }; // x ≥ 0
確かめたこと
- 修正後の
calc.js(コミット 7b1b403d)で、6,904通り(上の472通り × 1袋の量4種類、無作為5,000通り、正解表16件)がすべて正解と一致(2026-10-15) - 本番に反映したあと、本番の
calc.jsで同じ6,904通りの一致を確認 - シルギア側の記録では、テスト全体の227件がすべて通過