予測は 154.4cm なのに、目安は 149.3〜159.3cm——予測と目安が食い違った
父 150cm・母 145.7cm の男の子(c004)の予測は (150 + 145.7 + 13) ÷ 2 = 154.35cm、四捨五入して 154.4cm、目安はその ±5cm で 149.4〜159.4cm が正解です。
10言語中7言語が外れ、そのうち6言語(PHP・Go・JavaScript・C#・Rust・Swift)は、予測を 154.4 と正しく出したのに、目安を 149.3〜159.3 と出しました。予測と目安で、丸め方が食い違っています。公開中の元ツールも同じ食い違いを出します(父 150.0〜190.0cm・母 145.0〜175.0cm を 0.1cm 刻み・男女の241,402通り中48,280通り)。Python は予測も 154.3 と、すべて1つ下になりました。女の子(c005)でも同じ7言語が外れました。
父 171.2cm・母 170.1cm(c006)の 177.15cm と、父 183.1cm・母 171.2cm(c010)の 183.65cm は、10言語すべてが予測を 177.1・183.6 と1つ下に出しました。
なぜずれたのか
154.35 は、2進数の小数では正確に表せず、154.349999999999994… として入ります。177.15 は 177.149999999999977…、183.65 は 183.649999999999977… です。どれも 0.05 の境目のわずかに下なので、2進数の中身をそのまま丸めると1つ下になります。
違いは、丸める関数が「2進数の中身」を丸めるか、「短い10進の書き方」(154.35)を丸めるかで生まれました。
| 書き方 | 154.349999…(c004) | 149.349999…(c004 の目安) | 177.149999…(c006) |
|---|---|---|---|
PHP round・JavaScript Math.round(x*10)/10・Go math.Round(x*10)/10・C# Math.Round(x, 1)・Rust (x*10.0).round()・Swift (x*10).rounded() | 154.4 | — | 177.1 |
目安の書式:PHP sprintf・JavaScript toFixed(1)・Go・Python・C#・Rust・Swift の %.1f 系 | — | 149.3 | — |
Python round(x, 1) | 154.3 | — | 177.1 |
Java・Kotlin String.format("%.1f")・Ruby format("%.1f") | 154.4 | 149.4 | 177.1 |
- 10倍してから整数に丸める書き方(
x*10)は、154.349999… × 10 が 1543.5 ちょうどになるので、たまたま正しく 154.4 になりました。177.149999… × 10 は 1771.4999… のままなので、177.1 になりました - 小数1桁の書式(
%.1f・toFixed(1))は、2進数の中身を丸めるので、目安の 149.349999… を 149.3 にしました。予測と目安を別の方法で丸めたので、食い違いました - Java・Kotlin の
String.formatと Ruby のformatは、149.349999… を 149.4 にしました。短い10進の書き方(149.35)を丸めたのと同じ結果です。この2つは c004・c005 を通しましたが、c006・c010 では予測(JavaMath.round(x*10)・KotlinroundToLong()・Rubyround(1))が 1つ下になりました。Ruby は c006 で、予測 177.1・目安 172.2〜182.2 と、ここでも予測と目安が食い違いました
どう直したか
身長を2進数の小数に直さず、10進のまま計算しました。予測を1回だけ正確に四捨五入し、目安は丸めたあとの予測から ±5 します。こうすると、予測と目安が食い違うことはありません。
- Python:
fractions.Fraction、Go:big.Rat、Ruby:Rational(正確な分数) - Java・Kotlin:
BigDecimalとRoundingMode.HALF_UP、C#:decimalとMidpointRounding.AwayFromZero - JavaScript:
BigInt、PHP・Rust・Swift: 小数点の位置をそろえた整数