Hi Team,
Please help me to display the all decimals exist in numeric variables.
I am using below code to convert the numeric values to character to see how many decimals, but how the same values not populated in numeric variables AVAL and CHG.
How to display the all decimals for numeric variables.
data out;
set in;
aval_c=put(aval,best32.);
chg_c=put(chg,best32.);
run;
Thank you,
Rajasekhar.
What do you mean by "all decimals"?
Mathematically, this is impossible, because most numbers have an infinite number of decimals (e.g. 1/3).
Supply examples, and the intended (final) results.
Hi @raja777pharma,
@raja777pharma wrote:
How to display the all decimals for numeric variables.
data out;
set in;aval_c=put(aval,best32.);
chg_c=put(chg,best32.);run;
As a rule, none of the common numeric formats (BESTw., w.d, Ew., etc.) will show you "all decimals for numeric variables" in many cases. They all do some sort of rounding or truncation and thus conceal parts of the content of the variable. The easiest way to see what exactly is contained in a numeric variable is to display it in a format such as HEX16. or BINARY64. showing the internal (binary floating-point) representation. This is sufficient for exact comparisons of two values, but you see only hexadecimal or binary digits.
However, if you want to see all decimals, you need to
The good news is that all (non-missing) values of numeric variables have a finite decimal representation. So, cases like 1/3 with an infinite number of decimals do not occur. (As in the decimal system, 1/3 does not have a finite binary representation, whereas the internal representations are limited by the available 8 bytes for a numeric variable and hence are finite.)
Unfortunately, I have not yet coded a function that displays the exact decimal value contained in a numeric variable (but this has been on my to-do list for quite a while), so for the time being this is a partly manual process using the BINARY64. format, ChrisNZ's macro CONVERTBASE attached to his 2020 post Re: Converting a 19 length number to hex and then to a string (only needed if there's a substantial integer part, not for your small example numbers) and mainly the function BASECONVFP that I posted in the same thread: ...Converting-a-19-length-number-to-hex-and-then-to-a-string/m-p/654626/highlight/true#M196566.
Typically, several different values of a numeric variable share the same BEST32. representation -- where "several" can be a small number like 5, but also a number in the millions (!). Contrary to naïve expectations, BEST32. is not always the best choice if you want to see the most precise value possible with a common, built-in format. I've seen many cases where BEST16. (!) or a suitable w.d format was superior to BEST32. Also, the rarely used DECIMALCONV= system option has a substantial impact. With its default setting COMPATIBLE my Windows SAS 9.4M5 displays
23 different values as 3.76082040434571 11 different values as 4.04399223570895 184467 different values as -0.00029820160976 184467 different values as 0.00030661712631
in the BEST32. format. The DATA step below writes the first and last number of each of the four ranges and the neighboring internal representations with a different BEST32. display to the log:
1158 data _null_; 1159 input x hex16.; 1160 put x hex16. x best32.; 1161 cards; 400E162902165AC2 3.7608204043457 400E162902165AC3 3.76082040434571 400E162902165AD9 3.76082040434571 400E162902165ADA 3.76082040434572 40102D0C4CF69768 4.04399223570894 40102D0C4CF69769 4.04399223570895 40102D0C4CF69773 4.04399223570895 40102D0C4CF69774 4.04399223570896 BF338AFE295A46AF -0.00029820160975 BF338AFE295A46B0 -0.00029820160976 BF338AFE295D1742 -0.00029820160976 BF338AFE295D1743 -0.00029820160977 3F34182E87A7320C 0.0003066171263 3F34182E87A7320D 0.00030661712631 3F34182E87AA029F 0.00030661712631 3F34182E87AA02A0 0.00030661712632
(The left column is identical to the data lines after the CARDS statement.)
The smallest and largest internal representation displayed as 3.76082040434571 in BEST32. (400E162902165AC3 and 400E162902165AD9, resp.), i.e., like your first AVAL value, written in the decimal system, are:
3.760820404345709189186663934378884732723236083984375 3.760820404345718959149280635756440460681915283203125
The difference between these two numbers equals 11*2**-50=9.76996...E-15. As you can see, the BEST32. representation (ending in ...571) of the larger value is not mathematically rounded to ...572.
While those exact decimal representations are of interest to fans of long numbers (like me), the internal binary or hexadecimal representations are sufficient for most practical purposes, as mentioned earlier.
What possible reason would you have to convert "all decimal places" of a number to character?
The main point is they ARE the same values.
The issue is that you printed the original numbers using the BEST12. format and generated the string using the BEST32. format. So of course they LOOK different.
SAS can only store 15 decimal digits exactly. If you want a method to generate a character variable that will result in the same value when converted back into a number then perhaps you want something like this?
The basic idea is to figure out the magnitude of the number using the LOG base 10. Then use that information decide how many digits you need to generate using the normal numeric format rather than the BEST format.
data have;
input @1 B @1 raw $32.;
cards;
3.76082040434571
4.04399223570895
-0.000298201609761234
0.000306617126311234
3.76082040434571
37.6082040434571
376.082040434571
;
data want;
set have ;
exp = floor(log10(abs(b)));
bformat = cats('F',min(32,max(17,17-exp)),'.',min(31,max(0,14-exp)));
bstring = putn(b,bformat);
put raw exp= b= :best32. / bstring /;
run;
Results:
475 data want; 476 set have ; 477 exp = floor(log10(abs(b))); 478 bformat = cats('F',min(32,max(17,17-exp)),'.',min(32,max(0,14-exp))); 479 bstring = putn(b,bformat); 480 put raw exp= b= :best32. / bstring /; 481 run; 3.76082040434571 exp=0 B=3.76082040434571 3.76082040434571 4.04399223570895 exp=0 B=4.04399223570895 4.04399223570895 -0.000298201609761234 exp=-4 B=-0.00029820160976 -0.000298201609761234 0.000306617126311234 exp=-4 B=0.00030661712631 0.000306617126311234 3.76082040434571 exp=0 B=3.76082040434571 3.76082040434571 37.6082040434571 exp=1 B=37.6082040434571 37.6082040434571 376.082040434571 exp=2 B=376.082040434571 376.082040434571
So you can see the generated BSTRING values match exactly the original RAW strings read originally.
Thanks, @Tom, for pointing out that @raja777pharma's main issue was maybe just the difference between BEST12.- and BEST32.-formatted values. I hadn't thought it could be that simple.
@Tom wrote:
If you want a method to generate a character variable that will result in the same value when converted back into a number then perhaps you want something like this?
The basic idea is to figure out the magnitude of the number using the LOG base 10. Then use that information decide how many digits you need to generate using the normal numeric format rather than the BEST format.
I think even your fine approach cannot overcome the limitations of the common numeric formats (including the "normal" Fw.d format) that I mentioned in my previous post. In your examples the equality
input(bstring, 32.)=b
holds indeed. But often, as we all know, numeric values with that many decimals rather arise from calculations than from reading numeric literals. It appears that @raja777pharma's AVAL and CHG values are a case in point. They appear to be base-10 logarithms of rational numbers:
data have(keep=b);
input x y;
b=log10(y); output;
b=b-log10(x); output;
cards;
5769.24 5765.28
11058.23 11066.04
;
Using the above HAVE input dataset (whose values of b, viewed in BEST32. format, look exactly like the AVAL_C and CHG_C values from the initial post) the equality input(bstring, 32.)=b breaks down in all four cases (at least on my Windows SAS 9.4M5).
For "a method to generate a character variable that will result in the same value when converted back into a number" I would suggest using the RB8. format:
bstring=put(b, rb8.);
Then the equality
input(bstring, rb8.)=b
should hold for all possible numeric values of b (including ordinary and special missing values). The HEX16. format/informat (which, unlike the RB8. format, are available in CAS) should work as well, except for special missing values.
I suspect the intent was to have a human readable character string.
To account for differences between numbers generated by calculations and reading text strings then perhaps you could replace the original numeric value with the value generated by reading back in the generated string?
@Tom wrote:
... perhaps you could replace the original numeric value with the value generated by reading back in the generated string?
To avoid a loss of accuracy I would rather try to find a numeric literal that, read by SAS, exactly matches the original value. On my computer the literals below (with 16-17 significant digits) satisfy this condition for the example numbers (logarithms).
3.760820404345718 -0.00029820160976035126 4.043992235708956 0.00030661712631019354
For the two numbers close to zero the last decimal is not uniquely determined.
It might be possible to create a user-defined format (based on a user-defined function) that yields such literals. Maybe that would be what @raja777pharma needs.
Neither the decimal equivalents of the internal representations
3.760820404345718070970860935631208121776580810546875 -0.000298201609760351260547395213507115840911865234375 4.0439922357089557181097916327416896820068359375 0.00030661712631019355512762558646500110626220703125
nor the 32-digit results (logarithms) of the Windows calculator
3.7608204043457181236947220335419 -2.9820160976030282536702286682087e-4 4.0439922357089559295145311670842 3.0661712631004661141724628180561e-4
can substitute those literals.
You seem to be trying to handle numbers that SAS cannot exactly represent because it uses 64 bit binary numbers.
For example, that small negative number (-0.00029820160976035126) has more digits than SAS can exactly represent.
633 data test; 634 integer=29820160976035126 ; 635 max_int=constant('exactint'); 636 put (_all_) (=comma30./); 637 run; integer=29,820,160,976,035,128 max_int=9,007,199,254,740,992
Sure. Yet, reading this long literal -- and no shorter literal -- into a numeric variable by an assignment or INPUT statement (or by the INPUT function with informat 32.) yields the exact same internal representation as the expression log10(5765.28)-log10(5769.24). I know that too long numeric literals can lead to unexpected results.
Registration is now open for SAS Innovate 2025 , our biggest and most exciting global event of the year! Join us in Orlando, FL, May 6-9.
Sign up by Dec. 31 to get the 2024 rate of just $495.
Register now!
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.
Ready to level-up your skills? Choose your own adventure.