数値・単位・ビット
0.1+0.2がぴったり0.3にならないのはなぜ?
使うツール
合計は0.3のはずなのに、画面には0.30000000000000004と出る。小数点以下を二桁へ丸めたら見た目は直りましたが、それで問題が解決したとは限りません。どの段階で近い値を使い、どの段階で丸めるかによって、合計や比較の結果は変わります。末尾の数字だけを消す前に、何が起きているかを小さな式で確かめましょう。
ここでは高精度電卓と浮動小数点の内部表現を使います。前者は入力した十進数の四則演算、後者はbinary32やbinary64へ収めたときのビットを見る道具です。同じ値を入れても、答えている問いは違います。どちらか一方を『正しい電卓』と決めるのではなく、計算したい量に合う表現を選ぶために比べます。
まず、期待する式をそのまま計算する
0.1 + 0.2結果は0.3です。次に0.1+0.2-0.3を、演算子は半角の+と-で入力すると0になります。SheepDevのこの電卓は入力を十進数の分数として扱い、四則演算の途中で通常のNumberへ変換しません。ただし、割り切れない結果の表示には桁数と丸めの指定が必要です。『高精度』を『無限の桁を画面へ表示できる』という意味には取らないでください。
自分のプログラムと比べるときは、同じ入力文字列を使います。先に別の処理で丸めた数値をコピーしたなら、すでに比較条件が変わっています。計算器へ入る前のデータ、実際に評価した式、画面へ出す直前の丸めを分けて記録すると、『どこで違いが出たか』を追いやすくなります。
0.1を、有限の二進数へ詰めるとどうなる?
浮動小数点の内部表現で、入力方式を十進数、形式をbinary32にして0.1を入力します。保存された値の表示は0.10000000149011612、16進表現は3dcccccdになります。入力した0.1と全く同じ値ではなく、用意されたビットの中で近い値を選んでいることが見えます。これは文字列の入力ミスではなく、表現方法の制約です。
次に0.5を入れると、同じ形式でも値は0.5、16進表現は3f000000です。小数なら必ず誤差が出る、というわけではありません。十進数で有限桁の値でも二進数では有限桁にならないものがある、という点が重要です。0.1が難しいからといって、すべての小数演算が同じ大きさの誤差を持つとも考えないでください。
表示が0.1でも、内部が十進数の0.1とは限らない
形式をbinary64へ変えると、0.1の値欄は0.1と表示され、16進表現は3fb999999999999aになります。表示欄が短くなったからといって、十進数の0.1を内部で厳密に保存したとは限りません。人が読むための数値表示と、保存されたビット列を分けて見るために、このツールには両方があります。
この違いは比較処理で表に出ます。画面上で同じ0.3に丸められた二つの値でも、内部で完全一致するとは限りません。ただし、何でも同じ小さな定数を足せば解決するというものでもありません。許容差を使うなら、測っている量の大きさと必要な精度から条件を決めます。金額や件数の規則まで、浮動小数点の都合だけで変更しないことが大切です。
この電卓の桁数は、小数点以下の桁数ではない
高精度電卓の精度設定は有効数字の桁数です。例えば精度3で12345を入力すると、丸め方が四捨五入なら12300です。同じ精度で0.012345なら0.0123になります。『精度3だから小数点以下三桁にする』という解釈ではありません。結果を金額の固定小数表示へ使いたい場合は、この違いを確認してください。
12345
0.012345
1 / 3
(1 / 3) * 31÷3の表示は0.333ですが、(1÷3)×3は1になります。この電卓では式の途中の分数を保持してから最後の表示を作るためです。表示された0.333を手でコピーして3倍すれば0.999です。途中結果の見えている桁だけを次の式へ渡すと、元の計算と条件が変わることが、この例で分かります。
ちょうど半分のときに、結果が分かれる
精度2で1.25を計算し、四捨五入と偶数丸めを比べます。四捨五入は1.3、偶数丸めは1.2です。偶数丸めでは、ちょうど中間のときに最後に残す桁が偶数になる側を選びます。1.35なら両方とも1.4なので、一つの例が一致しただけでは同じ規則とは判断できません。
負数では『切り捨て』という日常語も曖昧になります。精度2の-1.29は、ゼロ方向なら-1.2、負の無限大方向なら-1.3です。絶対値を小さくするのか、数直線で小さい側へ動かすのかを区別します。仕様書で切り捨てとだけ書かれていたら、正数の例だけで決めず、負数を扱うかと具体的な期待値を確認する必要があります。
一件ずつ丸めるか、合計してから丸めるか
1.25を二件足す計算を考えます。それぞれを有効数字2桁の四捨五入で1.3にしてから足せば2.6です。先に1.25+1.25を計算してから同じ桁数へ丸めれば2.5です。丸め方式だけそろえても、処理する順序が違えば一致しません。税、料金、ポイントなどで結果が違うときは、商品単位か注文合計かという段階も確認します。
正しい順序は、この電卓が決めるものではありません。契約や計算仕様で定めた段階を再現する必要があります。一度丸めた値から元の端数を取り戻すことはできないため、比較実験では元の入力を残します。計算途中の丸めを減らすことが常に正しいとも限りません。規則が一件ずつの丸めを求めるなら、それを省くと別の計算になります。
再現できる報告には、式と丸め条件を残す
『小数がおかしい』だけでなく、入力文字列、評価した式、数値の形式、有効桁数、丸め方式、丸めた段階を残します。例えば『1.25を精度2・偶数丸めで出すと1.2』なら、他の人も同じ条件で確かめられます。見た目の桁を短くする修正だけでは、計算途中の型やデータ受け渡しの精度は変わりません。
試す順序は、簡単な足し算、割り切れない割り算、ちょうど半分、負数、合計前後の丸めです。この五つを分けると、保存表現の問題と業務上の端数規則を混ぜずに調べられます。電卓は指定した式を確かめる道具であり、入力された値が測定値として正確か、税や料金の制度に合うかまで判断するものではありません。
計算が正確でも、元の測定まで正確になるわけではない
センサーが0.1単位までしか表示しない値を三つ足したとします。高精度電卓は、その入力文字列に対して正確に計算できます。しかし、センサーが表示する前に丸めた部分を復元することはできません。結果の桁数を増やすことと、測定した対象について詳しく分かることは別です。10桁の答えが出ても元の測定精度が10桁へ上がるわけではありません。この電卓では、値の不確かさや測定器の誤差を自動計算していないので、入力の由来は別に確認します。
反対に、金額の0.10が契約上の正確な値なら、測定の近似値とは扱いが違います。同じ見た目の小数でも、契約で決まった値なのか、計器の表示なのか、別の画面で丸められた計算結果なのかによって、守るべき規則が変わります。単に浮動小数点を使わなければ全部解決するわけではありません。保存する形式、途中の計算、最後の表示という三段階に、入力元の意味を加えて考えると、どこへ精度や丸めの条件を書くべきかが整理できます。