見出し画像

プログラミングで少数点ありの変数比較にご用心あれ

Object-Oriented Programming (OOP) 学習を進めていて別なところにハマったのでそのメモ。「コンピューターは正確だ!😤」と思っているそこのあなたこそ必見です。

(約 2,200文字の記事です。)



double, float, decimal 打ち切り誤差の話

プログラマなら既に知っているとは思うが、プログラミング初心者にはハマりやすい罠だ。

本のサンプルコードを完成させていじっていたら、ときどき、こんな風に後ろにびろ~んと000000001が付いて表示されるときがある。いつもじゃないからタチが悪い。

// コーヒーの値段(ドル)
$1.25
$1.3900000000000001

1.25はいいのだが、なぜか1.39にはならず、1.390000000……001が付く。ちょっと前にdoubleとfloatについて調べていて知っていたのでChatGPT先生に聞いてみたらビンゴだった。


どんな言語を使っても根っこは一緒。避けられない「打ち切り誤差」

これに関する分かりやすい解説がPythonドキュメントにあったので貼っておきます。

ま、要するにどんなプログラミング言語でもdouble, floatなどの型ではどうしても打ち切り誤差があるよ、ということ。

数式が正しくても、その数式を忠実にコードで表現しても、変数に格納されている値は2進数表現だよ。なので場合によっては10進数の(想定した)計算結果とは異なるビット表現がその変数に格納されているかもよ、という罠。比較したときにtrueにならない可能性がある。

そして「ならば何でも10進数演算のdecimal型で押し通せばいいのか?」と言われればNoで、どうやら実行速度が遅いらしい。詳細はこちら。

要するに早いdoubleまたはfloatと誤差の処理実装で運用するか、処理速度よりも精度を優先してdecimalにするかは設計次第だね。金融処理関係などはdecimal一択らしい。


UIでの表示処理なら小数点以下カット表示でOK

んで、詰まったサンプルソースコードの、次の課題があったのでそれに挑戦していた。多分できたと思ったので答え合わせしたら、そっちのコードにはキッチリ「少数点以下2桁で切って表示させる」ようにしれっと1行が変わっていた。After参照。

Before、問題の1.3900……001が出る版。しょうがないので型を全部decimalにして10進数表現にして対処してみた。それはそれでOKだ。修行である👍

Console.WriteLine(beverage.GetDescription() + " $" + beverage.Cost());


After、本の解答。そりゃ下2桁で表示を打ち止めしているわけで……。
ずるい😭

Console.WriteLine(beverage.GetDescription()
                  + " $" + string.Format("{0:F2}", beverage.Cost()));

まぁ計算用ではなくてUIとしての数字表示用なのでこの打ち切り実装が一番ベストでしょうね。

とりあえず小数点以下の数値を厳密に変数同士で比較するためには一工夫して比較しないと痛い目を見るかもね。あるいは既存の比較用の関数を使うなどして車輪の再発明を避けよう。


ちなみにPythonでは「いい感じ」に誤差を丸め込んで表示されるファジー機能があるそうで。そういう点でもPythonは初心者向きとも言える。だが玄人にはそっちのほうが逆に分かりにくくなることもある。Pythonで学んでいてターミナルの表示結果でこういう現象に出くわさなかったことは実はPythonの内部処理のおかげだったのかもしれない。


少数点のある数字の変数の比較にご用心

小数点以下のある数値の比較には気を付けよう。
1.39と1.390000001はイコールにならない。比較結果はfalseだ。罠。

アルゴリズムがいくら正確でも、実装が正確でも、これはtrue, falseの挙動が理論と一致しないという罠。これは見つけづらい。


というわけで今回は「小数点以下のある変数の比較演算にご用心あれ」という結論でした😊


今日のデザインパターンの学習

Object-Oriented Programming (OOP) 学習を進めていて別なところにハマったのでそのメモ。

今日はdecoratorパターン1周目。どのパターンの章でもそうだが、1回で頭に入らない。大体3周して分かる気がする。

これまで学んだ内容

  • strategy pattern

  • observer pattern

  • decorator pattern ← 今ココ

ただし以前にUdemyのゾラン先生のC# のOOP教材を50時間かけて学んでいたので、その理解がこの本の理解に大いに役に立っている。

Head First本は冒頭にも書かれているように、全くのプログラミング初心者向けではない。

なので私はたまたまこのUdemyコースでゾラン先生からOOPの基礎をたたき込まれておいたのですんなり読み始められたが、基礎知識がなかったら多分既に1章で挫折していたと思う。

デコレーターパターンはどうやらこれ単体での運用よりも、次に登場するファクトリーパターンなどとの組み合わせで真価を発揮するらしい。やはりある程度のパターンを体得しないと使えないという予測は当たっているようだ。


OOPの話はこれまで。



今回の創作活動は約1時間(累積 約3,921時間)
(1,164回目のnote更新)


読んでくれてありがとう。気長にマイペースに書いてます。この出会いに感謝😊