最近經(jīng)常被一個問題苦惱,就是sas在保留小數(shù)位的時候經(jīng)常會出錯,我個人偏愛put,但put有的時候會出問題。所以我想,在保留時如果先把小數(shù)位放開到最多,然后先變字符型再變回數(shù)值,就可以有效保證put后不會出問題。
但如果這樣改的話會很麻煩,因為我已經(jīng)有很多寫好的宏,于是考慮是否可以用自定義函數(shù)。這樣,只要新函數(shù)的參數(shù)和put保持一致,就可以通過全文替換,直接完成修改。經(jīng)過測試,自定義函數(shù)可以用語sql,這是個好事。
想起之前我寫過一個保留有效數(shù)字的方法,突然想不如一起集合在一個函數(shù)上,于是新函數(shù)設(shè)計了3個參數(shù),前兩個和put一樣。第三個參數(shù)如果是0則為保留小數(shù),如果是1則是保留有效數(shù)字。當參數(shù)為1時,第二個參數(shù)只能是正整數(shù)。
proc fcmp outlib=work.funcs.putd;
? ? /* 定義函數(shù) */
? ? function putd(var, informat , roundOption) $200;
? ? ? ? length result $200; /* 假設(shè)轉(zhuǎn)換后的字符不會超過200個字符 */
/* informat 參數(shù)應(yīng)轉(zhuǎn)換為SAS格式名稱 */
? ? ? ? formatName = cats(informat);
? ? ? ? /* 根據(jù) roundOption 的值處理變量 */
? ? ? ? if roundOption = 0 then do;
? ? ? ? ? ? result = cats(putn(round(var,0.1**30), formatName)); /* 四舍五入 informat為數(shù)值型*/
? ? ? ? end;
? ? ? ? else if roundOption = 1 then do;/* 保留有效數(shù)字 informat為正整數(shù)*/
????????????length ornumc $200;
????????????if int(var)^=var then ornumc=strip(put(var,best.));
????????????else ornumc=cats(put(var,best.),'.');
????????????dot=index(strip(ornumc),'.');
????????????efb=prxmatch('/[1-9]/',strip(ornumc));
????????????efe=efb+informat;
????????????if efb < dot <= efe then efe=efe+1;
????????????if efe > dot > 1? then result=putn(var,cats('30.',efe-dot-1,'-l'));
????????????else if? dot > 1 then result=strip(putn(round(var,10**(dot-1-informat)),cats('32.',formatName)));
? ? ? ? end;
? ? ? ? else do;
? ? ? ? ? ? result = 'Invalid Option'; /* 無效的選項 */
? ? ? ? end;
? ? ? ? return(result); /* 返回結(jié)果 */
? ? endsub;
run;
quit;
/* 從編譯的函數(shù)庫中引用函數(shù) */
options cmplib=(work.funcs);
data _null_ aaaa;
? ? num_var = 123.456; /* 測試的數(shù)值 */
? ? char_var = putd(num_var, 12.1, 0); /* 調(diào)用函數(shù)進行轉(zhuǎn)換 */
? ? put char_var=; /* 輸出轉(zhuǎn)換結(jié)果 */
run;
本來想把putd函數(shù)的第3個參數(shù)設(shè)默認值的,后來發(fā)現(xiàn)無法設(shè)定。所以我自己已經(jīng)把上面這個拆成2個函數(shù)了。大家看自己需要吧。
再有就是這段程序會在work下生成一個名為Funcs的數(shù)據(jù)集,這個數(shù)據(jù)集存在,才可以使用新函數(shù)。建議定義的時候把這個數(shù)據(jù)集定義到一個其他邏輯庫下,這樣可以防止被誤刪。
************************************************************************************************
需要補充一點,本來我在下面一步中,打算使用put的:先用put變成字符,然后再用input換成數(shù)值,目的是先保留一個比較多的位數(shù),然后在此基礎(chǔ)上保留正確小數(shù)位。
result = cats(putn(round(var,0.1**30), formatName)); /* 四舍五入 informat為數(shù)值型*/
但是這個代碼卻有致命問題:
當數(shù)值和字符互換的時候,數(shù)值會發(fā)生損失,請看下面例子:
data _null_;
a=2.25;
b=put(a,32.25);
c=input(b,best32.);
d=a-b;
e=a=b;
f=a-c;
g=a=c;
put a=b=c=d=e=f=g=;
run;
結(jié)果為:A=2.25 B=2.2500000000000000000000000 C=2.25 D=4.440892E-16 E=0 F=4.440892E-16 G=0
可以看到,2.25通過put變成字符,再換回數(shù)值后,與原來的2.25相比,差值(F的值)不為0。這種情況原因不明,但可以肯定,會發(fā)生在本來小數(shù)位不多,但放出小數(shù)位很多的情況下。上例中,如果把b=put(a,32.25);換成b=put(a,32.24);,則F的值就會變成0,也就是說在2.25這個數(shù)中,25位就是一個極限值 。
但是極限值和數(shù)值有關(guān),如果把2.25換掉,則這個極限值也會改變。但兩者關(guān)系目前并不清楚,有機會我再測試一下。