Rounding numbers dynamically

It's often a requirement to round the values of one variable to a precision defined in a different variable - either another data set variable, or a macro variable. The easiest way to achieve this is:

cooked = round(raw, 10**-dp);

In this example, the required number of decimal places is set in the dataset variable dp. The ** exponentiation operator is used to convert from a number of decimal places to a suitable rounding unit for the round() function: for example, when dp is set to 3, 10**-dp becomes 10**-3, which is 10-3 or 0.001.

Here's an example log showing this is use:

2 data _null_; 3 x = ranuni(0) * 100; 4 do dp = 1 to 8; 5 y = round(x, 10**-dp); 6 put x= dp= y=; 7 end; 8 run; x=86.278556141 dp=1 y=86.3 x=86.278556141 dp=2 y=86.28 x=86.278556141 dp=3 y=86.279 x=86.278556141 dp=4 y=86.2786 x=86.278556141 dp=5 y=86.27856 x=86.278556141 dp=6 y=86.278556 x=86.278556141 dp=7 y=86.2785561 x=86.278556141 dp=8 y=86.27855614 NOTE: The data step took : real time : 00:00:00.000 cpu time : 00:00:00.000

The technique can be encapsulated in a function-style macro which can be called as %round(value, num_dp) instead of round(value, rounding_unit):

2 options mlogic mprint symbolgen; 4 %macro round(var, dp); 5 round(&var, 10**-&dp) 6 %mend; 8 data _null_; 9 x = ranuni(0) * 100; 10 y = %round(x, 4) MLOGIC(ROUND): Beginning execution of macro ROUND MLOGIC(ROUND): Parameter VAR has value x MLOGIC(ROUND): Parameter DP has value 4 SYMBOLGEN: Macro variable var resolved to x SYMBOLGEN: Macro variable dp resolved to 4 MPRINT(ROUND): round(x, 10**-4) MLOGIC(ROUND): Ending execution of macro ROUND 10 ! ; 11 put x= y=; 12 run; x=51.413577121 y=51.4136 NOTE: The data step took : real time : 00:00:00.020 cpu time : 00:00:00.010