Hi all,
Sorry it took me so long to come back to this, ended up with a job change and life turned a little hectic.
You are all right the original code was terrible and in completely the wrong direction. I learnt my lesson not to grab random code from the internet and start to hack it to pieces late on a Friday afternoon
To those of you who mentioned using the E format, thank you very much, using this did not even cross my mind (not sure I have ever used this format before). Your various suggestions were most helpful.
I recreated the function as below.
data test;
a=9999999.76403; OUTPUT;
a=2981248.76403; OUTPUT;
a=981248.76403; OUTPUT;
a=81248.76403; OUTPUT;
a=0.9999999999; OUTPUT;
a=9.98999; OUTPUT;
a=9.99999; OUTPUT;
a=12.76; OUTPUT;
a=2.76; OUTPUT;
a=12.7602; OUTPUT;
a=1248.76403; OUTPUT;
a=248.76403; OUTPUT;
a=48.76403; output;
a=8.76403; OUTPUT;
a=0.76403; OUTPUT;
a=0.76003; OUTPUT;
a=0.076403; OUTPUT;
a=0.0076403; OUTPUT;
a=0.00076403; OUTPUT;
a=0.000076403; OUTPUT;
a=0.00007640387106801; OUTPUT;
RUN;
DATA test;
SET test;
OUTPUT;
a=a*-1;
OUTPUT;
RUN;
PROC SORT data=test; BY descending a; RUN;
proc fcmp outlib=WORK.FUNCS.TEST;
function sigdigz(val, sig, len) $200;
LENGTH as_e as_t rhs wd outval $200; /* Set lengths for variables */
%LET ING=ING;
as_e = putn(val, cats('E', SIG+6, '.')); /* Print value using E notation */
as_t = put(val, best32.); /* Print value in full */
rhs=scan(as_e, 2, "E"); /* Get text from right hand side of E notation */
e=input(rhs, best.); /* And convert to numeric */
maxd=length(scan(as_t, 2, ".")); /* Find maximum number of decimal places in original value */
IF e+1<sig THEN d=min(sig-e-1, maxd); /* d will be number of values to display after decimal point, but cannot be more than original value */
else d=0;
IF d=0 THEN w=len; /* w is width of format, based on len and expanded for decimal values */
ELSE w=len+2+d;
wd=compress(put(w, best.)||"."||put(d, best.)); /* create format for printing value */
as_n=input(as_e, E32.); /* Input E text value as numeric */
outval=putn(as_n, wd); /* and print to text using format created above */
lx=len-length(scan(compress(outval), 1, ".")); /* Get length of integer part of value to check against planned len, issue warn if too great */
IF lx>len THEN PUT "WARN&ING: LENGTH value too small to correctly print value";
return(outval);
endsub;
run;
options cmplib=WORK.FUNCS;
DATA test6;
SET test;
c=sigdigz(a, 3, 9);
d=sigdigz(a, 4, 9);
e=sigdigz(a, 5, 9);
f=sigdigz(a, 6, 9);
g=sigdigz(a, 7, 9);
h=sigdigz(a, 8, 9);
RUN;
proc print data=test6; format a best32.;run;
The function now works as intended such that,
- All numbers are decimal aligned according the len value (characters prior to the decimal point)
- Trailing zeros are correctly kept (e.g. 0.76003 to 4s.f. is 0.7600, the trailing zero are shown as we know the value was captured with this precision)
- Values are not over ?signified? (e.g. 2.76 to 4s.f is still 2.76, we cannot assign this to 2.760 as that would be presenting to a higher precision than we actually know).
This is never true?
IF lx>len THEN PUT "WARN&ING: LENGTH value too small to correctly print value"; /* Get length of integer part of value to check against planned len, issue warn if too great */
sig=3 lx=8 len=9
a=123456789012.34 | b=1.23E11 |
You are right I think it should be
lx=length(scan(compress(outval), 1, ".")); /* Get length of integer part of value to check against planned len, issue warn if too great */
IF lx>len THEN PUT "WARN&ING: LENGTH value too small to correctly print value";
Save $250 on SAS Innovate and get a free advance copy of the new SAS For Dummies book! Use the code "SASforDummies" to register. Don't miss out, May 6-9, in Orlando, Florida.
Learn the difference between classical and Bayesian statistical approaches and see a few PROC examples to perform Bayesian analysis in this video.
Find more tutorials on the SAS Users YouTube channel.
Ready to level-up your skills? Choose your own adventure.