BookmarkSubscribeRSS Feed
SwissC
Obsidian | Level 7

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).

 

ChrisNZ
Tourmaline | Level 20

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

 

SwissC
Obsidian | Level 7

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";

  

hackathon24-white-horiz.png

The 2025 SAS Hackathon has begun!

It's finally time to hack! Remember to visit the SAS Hacker's Hub regularly for news and updates.

Latest Updates

What is Bayesian Analysis?

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.

SAS Training: Just a Click Away

 Ready to level-up your skills? Choose your own adventure.

Browse our catalog!

Discussion stats
  • 17 replies
  • 7195 views
  • 4 likes
  • 7 in conversation