背筋が凍る、厄介でおっかない小数点誤差の話

恐らく、誰しもが一度は経験していることだと思いますが、小数の計算というものはとてもやっかいです。
他の言語は詳しくないのでわかりませんが、SASにおいても、ある程度知っておかないと、直感では理解不能な現象に直面します。

今、以下のようなデータセットがあるとします。

data Q1;
A=0.2;B=0.1;output;
A=0.3;B=0.2;output;
run;






AからBを引いて、Cという変数に割り当てます

data Q2;
set Q1;
 C=A-B;
run;






それがどおした?当たり前じゃないかという感じですが、
知っている人は既にピンときているはず。


Cが0.1であれば変数FLに'Y'、それ以外は'N'をいれます。

data Q3;
 set Q2;
 if C=0.1 then FL='Y';
 else FL='N';
run;

当然、Cは2レコードとも0.1なわけだから、FLはYになると思いきや、、、








とまあ、こういうことになるんです。

これは、見た目に同じ0.1でも実は内部的に10の何乗分の1ぐらいの、超小さい部分で誤差が生じているんですね。
なんで、0.2-0.1と0.3-0.2みたいな小学生でもできる計算で誤差が生じるのかというと、SASが内部的に2進法に変換して計算しているため、パターンによって10進法に戻すときに微細な誤差がでちゃうんですね。
ごめんなさい、感覚で覚えているので、詳しい理屈は詳しい方に聞いてください。

見た目、両方、0.1じゃん!と思った方は

data Q4;
set Q3;
D=C;
format D HEX16.;
run;

のように例えば16進数のフォーマットを当ててみると





あ、確かになんか違う!って感じです。


この現象が往々にして猛威をふるいます。

if文などの条件分岐がこけたり、コンペアが一致しなかったり、ソート順がおかしく見えたりするわけです。

対応策としては、roundで

data Q5;
 set Q2;
 if round(C,0.00001)=0.1 then FL='Y';
 else FL='N';
run;

適当に余裕をもって丸めるとかの






原始的処理になります。

まあ、コンピューターってそういうもんだとは思いますが、何とかならないですかね、、、。





0 件のコメント:

コメントを投稿