As the first step in the decommissioning of sasCommunity.org the site has been converted to read-only mode.


Here are some tips for How to share your SAS knowledge with your professional network.


Difference between revisions of "Unintentional Loss of Precision"

From sasCommunity
Jump to: navigation, search
m (typo)
m (added example to show use of hex16)
Line 5: Line 5:
 
Here's an example. First compute a ratio and store it in a macro variable:
 
Here's an example. First compute a ratio and store it in a macro variable:
  
proc sql noprint;
+
<pre>
create table demo (fraction1 num);
+
proc sql noprint;
insert into demo set fraction1 = 1 / 3;
+
        create table demo (fraction1 num);
select fraction1 into : fraction2 from demo;
+
        insert into demo set fraction1 = 1 / 3;
quit;
+
        select fraction1  
 +
        into : fraction2  
 +
        from demo;
 +
        quit;
 +
</pre>
  
 
Now compare the value extracted from the macro variable to the value passed in the data set:
 
Now compare the value extracted from the macro variable to the value passed in the data set:
  
data _null_;
+
<pre>
set demo;
+
data _null_;
fraction2 = &fraction2;
+
set demo;
put (fraction : )( / = 16.14);
+
fraction2 = &fraction2;
run;
+
put (fraction : )( / = 16.14);
 +
run;
 +
</pre>
  
 
Result:
 
Result:
  
fraction1=0.33333333333333
+
<pre>
fraction2=0.33333300000000
+
fraction1=0.33333333333333
 +
fraction2=0.33333300000000
 +
</pre>
  
 
The behavior is similar when a DATA step rather than SQL is used to create the data.
 
The behavior is similar when a DATA step rather than SQL is used to create the data.
Line 28: Line 36:
 
One solution is to pass the value expressed as a hexadecimal string. This will preserve the value exactly:
 
One solution is to pass the value expressed as a hexadecimal string. This will preserve the value exactly:
  
proc sql noprint;
+
<pre>
create table demo (fraction1 num);
+
proc sql noprint;
insert into demo set fraction1 = 1 / 3;
+
        create table demo (fraction1 num);
select put(fraction1,hex16.) into : fraction2 from demo;
+
insert into demo set fraction1 = 1 / 3;
quit;                                                        <br>
+
select put(fraction1,hex16.) into : fraction2 from demo;
data _null_;
+
quit;                                                        <br>
set demo;
+
data _null_;
fraction2 = input("&fraction2",hex16.);
+
set demo;
put (fraction : )( / = best32.);
+
fraction2 = input("&fraction2",hex16.);
run;
+
put (fraction : )( / = best32.);
 +
run;
  
 
Result:
 
Result:
  
fraction1=0.33333333333333
+
<pre>
fraction2=0.33333333333333
+
fraction1=0.33333333333333
 +
fraction2=0.33333333333333
 +
</pre>
  
 
Note that this approach cannot be used successfully if the value is being passed from one host platform to another.
 
Note that this approach cannot be used successfully if the value is being passed from one host platform to another.
 +
 +
=== Passing Numeric Reals as hex16. in Macro Variables ===
 +
 +
solution posted by --macro maven == the radical programmer 11:47, 25 August 2008 (EDT)
 +
 +
<pre>
 +
Title2 demo-passing-hex16.sas;
 +
%Macro PutNumber(ConversionType=
 +
                ,Hex16 =
 +
                );
 +
%put Hex16:&Hex16.=%sysevalf(&Hex16.x);
 +
%local I Number Type1 Type2 Type3 Type4 Type5 Dim_Type;
 +
%Let Type1 =;
 +
%Let Type2 =boolean;
 +
%Let Type3 =ceil  ;
 +
%Let Type4 =floor  ;
 +
%Let Type5 =integer;
 +
%Let dim_Type=5;
 +
%do I = 2 %to &Dim_Type.;
 +
    %let Number = %sysevalf(&Hex16.x,&&Type&I.);
 +
    %put Number:&Number. : &&Type&I.;
 +
    %end;
 +
 +
Data  _Null_;
 +
attrib Number length = 8;
 +
Number = input("&Hex16.",hex16.);
 +
put Number= hex16. ' :: ' Number 32.15;
 +
stop;
 +
run;
 +
%Mend;
 +
 +
%Put %sysfunc(putn(1234.56,hex16.));
 +
%PutNumber(Hex16 = 40934A3D70A3D70A);
 +
%Put %sysfunc(putn(-1234.56,hex16.));
 +
%PutNumber(Hex16 = C0934A3D70A3D70A);
 +
%PutNumber(Hex16 = 0000000000000000);
 +
%PutNumber(Hex16 = 1111111111111111);
 +
%PutNumber(Hex16 = 3FF0000000000000);
 +
%PutNumber(Hex16 = 4000000000000000);
 +
%PutNumber(Hex16 = 4010000000000000);
 +
%PutNumber(Hex16 = 4020000000000000);
 +
%PutNumber(Hex16 = 4030000000000000);
 +
%PutNumber(Hex16 = 4040000000000000);
 +
%PutNumber(Hex16 = 4050000000000000);
 +
%PutNumber(Hex16 = 4060000000000000);
 +
</pre>
  
 
=== Other Scenarios Causing Unintentional Loss of Precision? ===
 
=== Other Scenarios Causing Unintentional Loss of Precision? ===

Revision as of 10:47, 25 August 2008

Passing Floating Point Values as Macro Variables

Macro variables can be handy for passing scalar values from one step to another. However, when the values have many significant digits, there can be loss of precision.

Here's an example. First compute a ratio and store it in a macro variable:

proc sql noprint;
         create table demo (fraction1 num);
         insert into demo set fraction1 = 1 / 3;
         select fraction1 
         into : fraction2 
         from demo;
         quit;

Now compare the value extracted from the macro variable to the value passed in the data set:

data _null_;
set  demo;
fraction2 = &fraction2;
put (fraction : )( / = 16.14);
run;

Result:

fraction1=0.33333333333333
fraction2=0.33333300000000

The behavior is similar when a DATA step rather than SQL is used to create the data.

One solution is to pass the value expressed as a hexadecimal string. This will preserve the value exactly:

proc sql noprint;
         create table demo (fraction1 num);
insert into demo set fraction1 = 1 / 3;
select put(fraction1,hex16.) into : fraction2 from demo;
quit;                                                         <br>
data _null_;
set demo;
fraction2 = input("&fraction2",hex16.);
put (fraction : )( / = best32.);
run;

Result:

<pre>
fraction1=0.33333333333333
fraction2=0.33333333333333

Note that this approach cannot be used successfully if the value is being passed from one host platform to another.

Passing Numeric Reals as hex16. in Macro Variables

solution posted by --macro maven == the radical programmer 11:47, 25 August 2008 (EDT)

Title2 demo-passing-hex16.sas;
%Macro PutNumber(ConversionType=
                ,Hex16 =
                );
%put Hex16:&Hex16.=%sysevalf(&Hex16.x);
%local I Number Type1 Type2 Type3 Type4 Type5 Dim_Type;
%Let Type1 =;
%Let Type2 =boolean;
%Let Type3 =ceil   ;
%Let Type4 =floor  ;
%Let Type5 =integer;
%Let dim_Type=5;
%do I = 2 %to &Dim_Type.;
    %let Number = %sysevalf(&Hex16.x,&&Type&I.);
    %put Number:&Number. : &&Type&I.;
    %end;

Data   _Null_;
attrib Number length = 8;
Number = input("&Hex16.",hex16.);
put Number= hex16. ' :: ' Number 32.15;
stop;
run;
%Mend;

%Put %sysfunc(putn(1234.56,hex16.));
%PutNumber(Hex16 = 40934A3D70A3D70A);
%Put %sysfunc(putn(-1234.56,hex16.));
%PutNumber(Hex16 = C0934A3D70A3D70A);
%PutNumber(Hex16 = 0000000000000000);
%PutNumber(Hex16 = 1111111111111111);
%PutNumber(Hex16 = 3FF0000000000000);
%PutNumber(Hex16 = 4000000000000000);
%PutNumber(Hex16 = 4010000000000000);
%PutNumber(Hex16 = 4020000000000000);
%PutNumber(Hex16 = 4030000000000000);
%PutNumber(Hex16 = 4040000000000000);
%PutNumber(Hex16 = 4050000000000000);
%PutNumber(Hex16 = 4060000000000000);

Other Scenarios Causing Unintentional Loss of Precision?