マクロ変数に数値をいれて戻すと誤差がでちゃう場合がある問題について考える話

SASプログラミング掲示板の方の話題で
http://tumesas.progoo.com/bbs/tumesas_topic_pr_20.html

話題の本筋ではないのですが
p値をマクロ変数に入れて、後でそこから値を取り出すと、本来の値と比べて凄い小さい誤差がでてるっぽいねという話がありました。

まあ、基本コンピューターの小数点表現には限界があるので、みんな、疑わしきはroundみたいな習慣がついてるので、問題ないね!ということです。
そういや、そもそも、僕の場合、何らかの計算結果をマクロ変数に入れるってこと自体、したことないかも。

けど、一応なんかないかな~と気になって調べてみると

hex16フォーマットを使うという記事がいくつか、ひっかかります
http://www.sascommunity.org/wiki/Unintentional_Loss_of_Precision とか)

読んでもあんまりピンとこないから、実際やってみました。

data _NULL_;
call symputx('A',1/2);
call symputx('B',1/3);
call symputx('C',1/7);
call symputx('D',sqrt(2));
call symputx('E',constant('pi'));
run;

と5つの計算結果をマクロ変数に入れてみます

%put A=&A B=&B C=&C D=&D E=&E;

とすると





お~、掲示板の話と違って、これはわかりやすく切れてますね。
そうか、なんもしないとデフォルトのフォーマットの有効桁数分しか文字に入らんわけか。
まあ一応、確認してみますか

data A1;
A=1/2;
B=1/3;
C=1/7;
D=sqrt(2);
E=constant('pi');

FL1=ifc(A=&A,'同じ','違う');
FL2=ifc(B=&B,'同じ','違う');
FL3=ifc(C=&C,'同じ','違う');
FL4=ifc(D=&D,'同じ','違う');
FL5=ifc(E=&E,'同じ','違う');

keep FL:;
run;

とすると





と当然の結果。

ではでは、hex16で文字化してからマクロ変数に入れて

data _NULL_;
call symputx('HA',put(1/2,hex16.));
call symputx('HB',put(1/3,hex16.));
call symputx('HC',put(1/7,hex16.));
call symputx('HD',put(sqrt(2),hex16.));
call symputx('HE',put(constant('pi'),hex16.));
run;

%put HA=&HA HB=&HB HC=&HC HD=&HD HE=&HE;




マクロ変数を戻す時に、hex16.で再び読み込んで比較すると

data A2;
A=1/2;
B=1/3;
C=1/7;
D=sqrt(2);
E=constant('pi');

FL1=ifc(A=input("&HA",hex16.),'同じ','違う');
FL2=ifc(B=input("&HB",hex16.),'同じ','違う');
FL3=ifc(C=input("&HC",hex16.),'同じ','違う');
FL4=ifc(D=input("&HD",hex16.),'同じ','違う');
FL5=ifc(E=input("&HE",hex16.),'同じ','違う');

keep FL:;
run;





となるんですね。

なんか、やっぱよくわかんないですね。
どんなフォーマット当てようが、どっかで切れてんのは一緒じゃないのかって
思ってしまうのですが…。
誰か詳しく解説してください。

掲示板の内容でもチェックしてみます。

proc univariate data=SASHELP.CLASS noprint;
 var AGE;
 output out=U1 probt=PT;
run;
data _NULL_;
 set U1;
 call symputx('VAL',put(PT,hex16.));
run;
data OUT1;
 set U1;
 VAL=input("&VAL",hex16.);
 if PT=VAL then FLG="同じ";
run;

とすると





やっぱり、できましたね。

へ~

3 件のコメント:

  1. 10進数を2進数に変換するプログラムを作ってみました。
    その結果、下のように小数第48桁目で不一致が見られ、「V=&V」で循環小数が
    崩れていることから、マクロ変数の方が精度がわずかに落ちているようです。
    でも、詳しい説明ではありませんね。

    %let V=%sysevalf(1/3);

    data _null_;
    V=1/3;
    *V=&V;
    VI=int(V); *整数部;
    VD=V-VI; *小数部;
    S=9999999999999999999; *商の初期値;
    length XX X $200;
    do while (0<S);
    S=int(VI/2); A=VI-S*2; VI=S;
    X=compress(X) || put(A,1.);
    end;
    XX=reverse(X);
    XX=compress(XX) || ".";
    do i=1 to 50;
    S=VD*2; A=int(S); VD=S-A;
    XX=compress(XX) || put(A,1.);
    end;
    put XX=;
    run;

    V=1/3, XX=0.01010101010101010101010101010101010101010101010101
    V=&V , XX=0.01010101010101010101010101010101010101010101010001

    返信削除
  2. 有難うございます!!面白いです!!%sysevalfでやって、いれても、精度落ちるんですね!なんかほんと、よくわからないですね。いつも有難うございます!刺激と勉強になってます!

    返信削除
  3. 上記プログラムにおいて、”VD=S-A"のところで2進数による丸めの誤差がでてくると思われます。ご注意下さい。

    返信削除