BookmarkSubscribeRSS Feed
☑ This topic is solved. Need further help from the community? Please sign in and ask a new question.
whymath
Lapis Lazuli | Level 10

42 in decimal, is 101010 in binary, 2A in hex, 52 in octal, or 222 in quaternary.

Is there a macro of function can do any base conversion of numbers?

1 ACCEPTED SOLUTION

Accepted Solutions
FreelanceReinh
Jade | Level 19

Hello @whymath,

 

For positive integers I'm aware of @ChrisNZ's macro CONVERTBASE attached to his 2020 post https://communities.sas.com/t5/SAS-Programming/Converting-a-19-length-number-to-hex-and-then-to-a-st.... For fractions between 0 and 1 I wrote a function BASECONVFP (using ideas from ChrisNZ's macro) and posted it in the same thread: https://communities.sas.com/t5/SAS-Programming/Converting-a-19-length-number-to-hex-and-then-to-a-st.... In both cases allowable bases are 2, 3, ..., 36 and numbers with many (e.g. 100) digits are no problem.*

 

For the conversion of positive binary, hexadecimal and octal integers of moderate size to and from the decimal system there are the SAS-supplied BINARY, HEX and OCTAL informats and formats.

 

* Edit: And of course you can combine the macro and the function to convert non-integer numbers >1, as shown in the code example (log) below:

1    data _null_;
2    n=42;
3    put n binary6. / n hex2. / n octal2.;
4    call symputx('dec',n+0.38);
5    run;

101010
2A
52
NOTE: DATA statement used (Total process time):
      real time           0.11 seconds
      cpu time            0.11 seconds


6
7    %put &=dec;
DEC=42.38
8    %put %ConvertBase(10,4,%scan(&dec,1)).%sysfunc(baseconvfp(%scan(&dec,2),10,4,61));
222.1201101322320110132232011013223201101322320110132232011013223

View solution in original post

8 REPLIES 8
FreelanceReinh
Jade | Level 19

Hello @whymath,

 

For positive integers I'm aware of @ChrisNZ's macro CONVERTBASE attached to his 2020 post https://communities.sas.com/t5/SAS-Programming/Converting-a-19-length-number-to-hex-and-then-to-a-st.... For fractions between 0 and 1 I wrote a function BASECONVFP (using ideas from ChrisNZ's macro) and posted it in the same thread: https://communities.sas.com/t5/SAS-Programming/Converting-a-19-length-number-to-hex-and-then-to-a-st.... In both cases allowable bases are 2, 3, ..., 36 and numbers with many (e.g. 100) digits are no problem.*

 

For the conversion of positive binary, hexadecimal and octal integers of moderate size to and from the decimal system there are the SAS-supplied BINARY, HEX and OCTAL informats and formats.

 

* Edit: And of course you can combine the macro and the function to convert non-integer numbers >1, as shown in the code example (log) below:

1    data _null_;
2    n=42;
3    put n binary6. / n hex2. / n octal2.;
4    call symputx('dec',n+0.38);
5    run;

101010
2A
52
NOTE: DATA statement used (Total process time):
      real time           0.11 seconds
      cpu time            0.11 seconds


6
7    %put &=dec;
DEC=42.38
8    %put %ConvertBase(10,4,%scan(&dec,1)).%sysfunc(baseconvfp(%scan(&dec,2),10,4,61));
222.1201101322320110132232011013223201101322320110132232011013223
whymath
Lapis Lazuli | Level 10

Thanks, @FreelanceReinhard, Very impressive discussion.

The only weird thing is I run your example from <baseconvfp.sas> and get nothing output in euc-cn enviroment, but get right output in unicode enviroment. How do you think of that?

FreelanceReinh
Jade | Level 19

@whymath wrote:

The only weird thing is I run your example from <baseconvfp.sas> and get nothing output in euc-cn enviroment, but get right output in unicode enviroment. How do you think of that?


The BASECONVFP function uses the SAS character functions LENGTH, CHAR, RANK, BYTE and SUBSTR, all of which are "designed for SBCS [single-byte character set] data" (documentation), so it's well possible that problems arise in a MBCS (multi-byte character set) environment. I hadn't thought of this possibility during development. Glad to hear that it works in a Unicode environment nevertheless. I assume that a version not relying on the ASCII collating sequence would require a comprehensive revision of the code.

whymath
Lapis Lazuli | Level 10

Hi @FreelanceReinhard , When I use hex. or octal. format, I always need to specify the length of format or I will get many leading zeros in result. Like:

1    data _null_;
2      n=42;
3      put n hex.;
4    run;

0000002A

Now I try use substr() and findc() function to remove the leading zeros, just like:

5    data _null_;
6      n=42;
7      hex=put(n,hex.);
8      hexc=substr(hex,findc(hex,'123456789'));
9      put hexc;
10   run;

2A

But do you have an easy way to slove this?

FreelanceReinh
Jade | Level 19

@whymath wrote:

Now I try use substr() and findc() function to remove the leading zeros, just like:

5    data _null_;
6      n=42;
7      hex=put(n,hex.);
8      hexc=substr(hex,findc(hex,'123456789'));
9      put hexc;
10   run;

2A

You would need to extend the second argument of FINDC to '123456789ABCDEF' to make this work in general (e.g., for n=160). It's easier to use the VERIFY function:

hexc=substr(hex,verify(hex,'0'));

 

Another option is the PUTN function where you can supply the format width (as a function of n) at run time:

hex=putn(n,cat('hex',int(log2(n)/4)+1));

and similarly 

oct=putn(n,cat('octal',int(log2(n)/3)+1));
RichardDeVen
Barite | Level 11

There is not a SAS function for performing arbitrary radix to radix conversion.

 

You can write your own in Proc FCMP.  For an arbitrary value representation in the originating radix, you would need to supply a list of glyphs representing the digits of the radix - likewise for the destination radix.  You can find the use of base 5, 10, 12, 20, 60 in antiquity and nominally angular radixes of 360, 400 and π.  A V7 SAS Name could be construed in a numeric sense as base 37.

 

Example:

Simplified for representing integer number in an integer radix from 2 to 33 using glyphs 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_

 

options cmplib = ();

proc fcmp outlib=sasuser.utility.number;
  function IntToRadix(in_integer, in_radix) $33;

    integer = abs(floor(in_integer));
    radix = floor(in_radix);

    glyphs = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_';

    if radix > length(glyphs) or radix < 2 then
      return (catx(' ', 'Radix', radix, 'not implemented.'));

    sign = sign(in_integer);

    length result $33;
    result = '';

    if radix > 1 then do place = 1 by 1 while (integer > 0);
      remainder = mod(integer,radix);
      result = cats(substr(glyphs,remainder+1,1), result);
      integer = (integer - remainder) / radix;

      if place >=7 then 
        leave;

    end;
    else do; 
      /* todo, stick marks for base 1*/
    end;

    if missing (result) then
      result = '0';
    else
    if sign < 0 then
      result = '-' || result;

    return (result);
  endsub;

quit;

options cmplib = sasuser.utility;

data representations;
  do radix = 2 to 33;
    do num = 0 to 100;
      representation = IntToRadix(num,radix);
      output;
    end;
  end;
run;

proc sort; by num radix;
proc transpose data=representations out=reptable prefix=radix_;
  by num;
  var representation;
  id radix;
run;
whymath
Lapis Lazuli | Level 10
Your code always amazing me.
Could you please explain the meaning of "if place >=7 then leave;"?
RichardDeVen
Barite | Level 11
Ahhhh... if place >=7 then leave; is a developmental artifact that stopped the algorithm at the 7th representational glyph -- essentially a guard against a run-away loop while testing. It should be removed from the code.

sas-innovate-2024.png

Available on demand!

Missed SAS Innovate Las Vegas? Watch all the action for free! View the keynotes, general sessions and 22 breakouts on demand.

 

Register now!

How to Concatenate Values

Learn how use the CAT functions in SAS to join values from multiple variables into a single value.

Find more tutorials on the SAS Users YouTube channel.

Click image to register for webinarClick image to register for webinar

Classroom Training Available!

Select SAS Training centers are offering in-person courses. View upcoming courses for:

View all other training opportunities.

Discussion stats
  • 8 replies
  • 664 views
  • 6 likes
  • 3 in conversation