Hello!
I was wondering if someone could explain to me why my code isn't working correctly. I think it's something to do with how the SAS processor handles macro variables but I'm not entirely sure.
data test;
input unformatted_var $;
DATALINES;
1
;
run;
%let format = not_a_format;
data test;
set test;
have_format = 0;
if have_format then do;
formatted_var = put(unformatted_var, &format.);
end; else do;
formatted_var = unformatted_var;
end;
run;
PROC PRINT DATA = test; run;
The code is a simplified version of my main code which runs within a macro. It's purpose is to take unformatted_var and output it as formatted_var. Sometimes the code will have a format stored in the "format" macro
variable in which case a format is applied to the formatted _var, other times there is no format supplied and the formatted_var is the same as unformatted_var.
In the case above there is no format supplied so the have_format variable is set to 0. However despite this the code produces an error because it is trying to execute the line
formatted_var = put(unformatted_var, &format.);
Which can't run correctly because the "format" macro variable does not contain a format, even though that line should not be executed within the data step.
I am able to solve my problem by coding it differently, but I'm not sure why it's not working in the first place!
Thanks in advance.
The correctness of SAS syntax gets checked during compilation time. Because the format doesn't exist this leads to an error.
The IF conditions comes only into play during execution time - and that's after the syntax check during compilation time.
On top of this: A format needs to end with a dot. Resolving a macro variable consumes a dot. So even if there is a format supplied in your macro variable you need to use two dots for the resolved value of the macro variable (the format name) still to end with a dot.
formatted_var = put(unformatted_var, &format..);
You could avoid the error for a non existing format via option
options nofmterr;
Or - and better in my opinion - don't generate the SAS code at all if there is no value supplied for the format (assuming that macro variable &format exists but is empty if no format gets supplied).
data test;
input unformatted_var $;
DATALINES;
1
;
%let format =;
%macro doit();
data test;
set test;
%if %bquote(&format) = %bquote() %then
%do;
formatted_var = unformatted_var;
%end;
%else
%do;
formatted_var = put(unformatted_var, &format..);
%end;
run;
%mend;
%doit()
PROC PRINT DATA = test;
run;
If you're on a very recent SAS version then you can use SAS macro %IF %then %else even without a macro definition.
data test;
input unformatted_var $;
DATALINES;
1
;
%let format =;
data test;
set test;
%if %bquote(&format) = %bquote() %then
%do;
formatted_var = unformatted_var;
%end;
%else
%do;
formatted_var = put(unformatted_var, &format..);
%end;
run;
PROC PRINT DATA = test;
run;
Right now, SAS interprets the dot in &format. as the end of the macro variable. Not the end of a format specification. Therefore, put an extra dot efter your format macro variable
data test;
set test;
have_format = 0;
if have_format then do;
formatted_var = put(unformatted_var, &format..);
end; else do;
formatted_var = unformatted_var;
end;
run;
This will issue a note, not an error, that the format was not found because the Put Function looks for the format at compile time.
The correctness of SAS syntax gets checked during compilation time. Because the format doesn't exist this leads to an error.
The IF conditions comes only into play during execution time - and that's after the syntax check during compilation time.
On top of this: A format needs to end with a dot. Resolving a macro variable consumes a dot. So even if there is a format supplied in your macro variable you need to use two dots for the resolved value of the macro variable (the format name) still to end with a dot.
formatted_var = put(unformatted_var, &format..);
You could avoid the error for a non existing format via option
options nofmterr;
Or - and better in my opinion - don't generate the SAS code at all if there is no value supplied for the format (assuming that macro variable &format exists but is empty if no format gets supplied).
data test;
input unformatted_var $;
DATALINES;
1
;
%let format =;
%macro doit();
data test;
set test;
%if %bquote(&format) = %bquote() %then
%do;
formatted_var = unformatted_var;
%end;
%else
%do;
formatted_var = put(unformatted_var, &format..);
%end;
run;
%mend;
%doit()
PROC PRINT DATA = test;
run;
If you're on a very recent SAS version then you can use SAS macro %IF %then %else even without a macro definition.
data test;
input unformatted_var $;
DATALINES;
1
;
%let format =;
data test;
set test;
%if %bquote(&format) = %bquote() %then
%do;
formatted_var = unformatted_var;
%end;
%else
%do;
formatted_var = put(unformatted_var, &format..);
%end;
run;
PROC PRINT DATA = test;
run;
Thanks for the replies everyone. I didn't realise SAS checked syntax before executing, I'll take this into account with my code.
Using PUTC/PUTN as @Kurt_Bremser suggests is another option closer to the code you've posted as there the check of the format only happens during execution time. For this reason PUTC/PUTN also doesn't perform as good as PUT
If you want to conditionally use a format, use either putc or putn:
data test;
set test;
have_format = 0;
if have_format then do;
formatted_var = putc(unformatted_var, "&format..");
end; else do;
formatted_var = unformatted_var;
end;
run;
Since the format can be dynamic with these functions, the check occurs during runtime only when the function is actually called.
Join us for SAS Innovate 2025, our biggest and most exciting global event of the year, in Orlando, FL, from May 6-9. Sign up by March 14 for just $795.
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.