- Mark as New
- Bookmark
- Subscribe
- Mute
- RSS Feed
- Permalink
- Report Inappropriate Content
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?
Accepted Solutions
- Mark as New
- Bookmark
- Subscribe
- Mute
- RSS Feed
- Permalink
- Report Inappropriate Content
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
- Mark as New
- Bookmark
- Subscribe
- Mute
- RSS Feed
- Permalink
- Report Inappropriate Content
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
- Mark as New
- Bookmark
- Subscribe
- Mute
- RSS Feed
- Permalink
- Report Inappropriate Content
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?
- Mark as New
- Bookmark
- Subscribe
- Mute
- RSS Feed
- Permalink
- Report Inappropriate Content
@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.
- Mark as New
- Bookmark
- Subscribe
- Mute
- RSS Feed
- Permalink
- Report Inappropriate Content
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?
- Mark as New
- Bookmark
- Subscribe
- Mute
- RSS Feed
- Permalink
- Report Inappropriate Content
@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));
- Mark as New
- Bookmark
- Subscribe
- Mute
- RSS Feed
- Permalink
- Report Inappropriate Content
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;
- Mark as New
- Bookmark
- Subscribe
- Mute
- RSS Feed
- Permalink
- Report Inappropriate Content
Could you please explain the meaning of "if place >=7 then leave;"?
- Mark as New
- Bookmark
- Subscribe
- Mute
- RSS Feed
- Permalink
- Report Inappropriate Content