/**********************************************************************/


   /* Example 1 */

data _null_;                                   
   x=0;                                        
   do i=1 to 10;                               
      x+0.1;                                   
      put x=hex16. '(hex)' x='(formatted dec)';
   end;                                        
   if x=1 then put 'x is equal to 1';          
   else put 'what happened?';                  
run;


/**********************************************************************/


   /* Example 2 */

 data _null_;                                                 
    base=16;       /* 16 for IBM, 2 for VAX and IEEE       */  
    max_digt=14;   /* 14 for IBM, 56 for VAX, 53 for IEEE  */  

       /* Compute maximum integer and display              */  
    max_int=base ** max_digt;                                 
    put @2 max_int=  hex16. '(hex)' max_int  comma22. '(dec)';

       /* This DO loop attempts to store the five          */  
       /* integers before and after the maximum. Notice    */  
       /* that some of the integers after the maximum      */  
       /* cannot be stored exactly.                        */  
    put / @2 'I'  @8 'MAX_INT+I (in hex)' @31 'MAX_INT+I      
        (in dec)'                                             
        / @1 '---' @8 '---------------' @31 '---------------';
    do i=-5 to 5;                                             
       x=max_int+i;                                           
       put @1 i 2. @10 x hex16. @30 x comma22.;               
    end;                                                      
 run;                                                         


/**********************************************************************/


   /* Example 3 */

data _null_;                                               

      /* The first expression does not result in a value   */
      /* of zero.                                          */
   x= -16-0.1+16+0.1;                                      
   put x= hex16. x= '<= expression 1'/;                    

     /* By breaking apart the expression, you can see      */
     /* where representation error is introduced and       */
     /* where loss of significance occurs.                 */
   x= -16;                                                 
   put x= hex16. x=;                                       
   x= x-0.1;                                               
   put x= hex16. x= '<= representation error';             
   x= x+16;                                                
   put x= hex16. x= '<= loss of significance';             
   x= x+0.1;                                               
   put x= hex16. x= /;                                     

     /* If you change the order of the operands, the       */
     /* expression will evaluate correctly.                */
   x= 16-16+0.1-0.1;                                       
   put x= hex16. x= '<= expression 2';                     
run;                                                       


/**********************************************************************/


   /* Example 4 */

data _null_;                         

      /* IBM systems                                       */              
   x=255.115;                        
   x_rnd=round(x,.01);               
   y=256.115;                        
   y_rnd=round(y,.01);               
   put @10 'IBM systems' / _all_ / ; 

      /* VAX systems                                       */              
   x=4095.175;                       
   x_rnd=round(x,.01);               
   y=4096.175;                       
   y_rnd=round(y,.01);               
   put @10 'VAX systems' / _all_ / ; 

      /* IEEE systems                                      */             
   x=255.155;                        
   x_rnd=round(x,.01);               
   y=256.155;                        
   y_rnd=round(y,.01);               
   put @10 'IEEE systems' / _all_ / ;
run;                                 


/**********************************************************************/


   /* Example 5 */

%macro eqfuzz(var1, var2, fuzz=1e-12);          
    abs((&var1 - &var2) / &var1) < &fuzz        
%mend;                                          

data _null_;                                    
   x=0;                                         
   y=1;                                         
   do i=1 to 10;                                
      x+0.1;                                    
   end;                                         
   if x=y then put 'x exactly equal to y';      
   else if %eqfuzz(x,y) then put 'x close to y';
        else put 'x nowhere close to y';        
run;                                            


/**********************************************************************/


   /* Example 6 */

%macro round(var,unit,base=16,fz_digit=12);

       /*  Macro parameters are as follows:                */
       /*  var:   variable to be rounded                   */
       /*  unit:  round-off unit                           */
       /*  base:  base used for numeric representation     */
       /*         on the platform being used               */
       /*         (base 16 for IBM, base 2 for VAX         */
       /*         and IEEE)                                */
       /*  fz_digit: digit in representation where         */
       /*  fuzz occurs                                     */
       /*         (for IBM: maximum value is 14            */
       /*                   suggested value is 12          */
       /*          for VAX: maximum value is 56            */
       /*                   suggested value is 50          */
       /*          for IEEE: maximum value is 53           */
       /*                   suggested value is 48).        */
       /*         Note: Suggested value is less than       */
       /*         maximum to allow for some loss of        */
       /*         significance error as well as            */
       /*         representation error.                    */
       /*                                                  */
       /*  Fuzz factor is computed as follows:             */
       /*    The log in the base of the representation     */
       /*    is determined by taking the natural log of    */
       /*    the value and dividing it by the natural      */
       /*    log of the base. The resulting value is       */
       /*    raised to the next integer to determine the   */
       /*    magnitude of the value. The fz_digit          */
       /*    parameter is subtracted from the magnitude    */
       /*    of the value to determine the magnitude of    */
       /*    the fuzz factor. The base is then raised to   */
       /*    this magnitude and multiplied by the sign     */
       /*    of the value to create the actual fuzz        */
       /*    factor. This fuzz factor is added to the      */
       /*    variable value, and the ROUND function        */
       /*    is called.                                    */
round((&var+                                               
      (sign(&var)*                                         
      &base**(ceil(log(abs(&var))/log(&base))-&fz_digit))),
      &unit)                                               
%mend round;                                               


/**********************************************************************/


   /* Example 7 */

data _null_;                        

      /* IBM systems                                       */             
   x=255.115;                       
   x_rnd=%round(x,.01);             
   y=256.115;                       
   y_rnd=%round(y,.01);             
   put @10 'IBM systems' / _all_ / ;

      /* VAX systems                                       */             
   x=4095.175;                      
   x_rnd=%round(x,.01);             
   y=4096.175;                      
   y_rnd=%round(y,.01);             
   put @10 'VAX systems' / _all_ / ; 
      /* IEEE systems */             
   x=255.155;                        
   x_rnd=%round(x,.01);              
   y=256.155;                        
   y_rnd=%round(y,.01);              
   put @10 'IEEE systems' / _all_ / ;
run;                                 


/**********************************************************************/


   /* Example 8 */

%macro eqfuzz(var1,var2,fuzz=1e-3);
    abs(&var1-&var2)<=&fuzz        
%mend;                             


data a;                           
   input byval a_val $ a_group $; 
   cards;                         
1.0001 A1.0001 A-1-1              
1.9999 A1.9999 A-2-1              
4.0000 A4.0000 A-4-1              
4.9999 A4.9999 A-5-1              
5.0000 A5.0000 A-5-2              
5.9999 A5.9999 A-6-1              
6.0001 A6.0001 A-6-2              
7.0000 A7.0000 A-7-1              
7.0001 A7.0001 A-7-2              
7.0001 A7.0001 A-7-3              
;                                 

data b;                           
   input byval b_val $ b_group $; 
   cards;                         
2.0001 B2.0001 B-2-1              
3.0000 B3.0000 B-3-1              
3.9999 B3.9999 B-4-1              
4.0001 B4.0001 B-4-2              
4.0001 B4.0001 B-4-3              
5.0000 B5.0000 B-5-1              
5.0001 B5.0001 B-5-2              
6.0000 B6.0000 B-6-1              
8.0000 B8.0000 B-8-1              
;                                                          
run;                                                       


data c;                                                    
  length a_val b_val a_group b_group $ 8;                  

     /* Get next BY value from each data set.              */
  retain a_count b_count 1;                                
  if a_count<=a_nobs then                                  
     set a(keep=byval rename=(byval=next_a))               
     point=a_count nobs=a_nobs;                            
  if b_count<= b_nobs then                                 
     set b(keep=byval rename=(byval=next_b))               
     point=b_count nobs=b_nobs;                            

     /* Determine which data set needs to be advanced to   */
     /* merge current BY group.                            */
  if %eqfuzz(next_a,next_b) then                           
     do;                                                   
        read_a=1;                                          
        read_b=1;                                          
     end;                                                  
  else if next_a < next_b then                             
       do;                                                 
          read_a=1;                                        
          read_b=0;                                        
       end;                                                
  else do;                                                  
        read_a = 0;                                         
     read_b = 1;                                            
  end;                                                      

     /* If the end of one of the data sets has been        */ 
     /* reached, then get the remaining observations       */ 
     /* from the other data set.                           */ 
  if a_count>a_nobs then                                    
     do;                                                    
        read_a=0;                                           
        read_b=1;                                           
     end;                                                   
  if b_count > b_nobs then                                  
     do;                                                    
        read_a=1;                                           
        read_b=0;                                           
     end;                                                   

     /* Check for beginning of a new BY group.             */
  if (read_a and not (%eqfuzz(next_a,byval))) or            
     (read_b and not (%eqfuzz(next_b,byval))) then          
     do;                                                    

           /* Reset variables to missing.                  */ 
        a_val=' ';                                          
        b_val=' ';                                          
        a_group=' ';                                        
        b_group=' ';                                        
     end;                                                   

     /* Advance data set and write merged observation.     */
  if read_a then                                            
     do;                                                    
        set a;                                              
        a_count+1;                                          
        end;                                                
  if read_b then                                            
     do;                                                    
        set b;                                              
        b_count+1;                                          
     end;                                                   
     drop next_a next_b read_a read_b;                      
  run;                                                      


data a2;              
   set a;             
   byval=round(byval);
data b2;              
   set b;             
   byval=round(byval);
data c2;              
   merge a2 b2;       
   by byval;          
run;                  


proc sql;                                                
   create table d as                                     
          select a.byval, a_val, b_val, a_group, b_group 
          from a full join b                             
          on %eqfuzz(a.byval,b.byval);                   
   create table d2 as                                    
          select a2.byval, a_val, b_val, a_group, b_group
          from a2 full join b2                           
          on a2.byval=b2.byval;                          


/**********************************************************************/