Chapter 9: Arithmetic

9.1 Basic Rules

9.2 Truth in Numbers, Expressions, and Comparisons

9.2.1 Programming Challenge #7

9.2.2 Solution

The basics may bore you, but the details will delight you.

9.1 Basic Rules

The %LET statement automatically ignores arithmetic:

%let total =  3 * 5   ;

Here, &TOTAL is five characters long, and the third character is an asterisk. As usual, leading and trailing blanks are ignored. For the %LET statement to perform arithmetic, it must apply a function:

%let total = %eval(3 * 5);           15

%let total = %sysevalf(3 * 5);     15

The %EVAL function performs integer arithmetic, dropping any remainders:

%let total = %eval(10 / 4);    2

%let total = %eval(2.5 * 4);   error, decimal points not allowed

%let total = %eval(10./4.);    error, decimal points not allowed

Permissible variations include negative integers and parentheses to influence the order of operations:

%let total = %eval(-10 / (4 + 3));    -1

The %SYSEVALF function was invented later, to compute using decimal fractions:

%let total = %sysevalf(10 / 4);    2.5

%let total = %sysevalf(2.5 * 4);   10

Finally, %SYSEVALF supports a second parameter, indicating an operation to perform on the calculated result. Some examples:

%let total = %sysevalf(-10/3, floor);        -4

%let total = %sysevalf(-10/3, ceil);          -3

%let total = %sysevalf(-10/3, int);           -3

%let total = %sysevalf(-10/3, Boolean);   1

Boolean transformations return 0 or 1, depending on the result calculated by %SYSEVALF:

0: the result was 0 or missing

1: the result was any other value

Boolean transformation can prevent division by zero. For example:

%if %sysevalf(&denominator, Boolean) %then 

%let ratio = %sysevalf((&numerator) / (&denominator)); 

The first %SYSEVALF returns either a 1 (which %IF interprets as "true") or a 0 (which %IF interprets as "false"). And 1 corresponds exactly to the cases where division is possible: a nonmissing, nonzero denominator.

Take a quick challenge before moving on. Why use extra parentheses around &NUMERATOR and &DENOMINATOR? How is it possible that removing the parentheses would generate a different result?

The simplest answer is that one or both macro variables might contain an expression rather than a hard-coded value. Consider this combination:

%let numerator = 5 + 10; 

%let denominator = 10 + 5; 

Now removing parentheses changes the result:

(&numerator) / (&denominator)  (5 + 10) / (10 + 5)     1

&numerator  /  &denominator        5 + 10  /   10 + 5    11

9.2 Truth in Numbers, Expressions, and Comparisons

Both %EVAL and %SYSEVALF can evaluate expressions as being true or false, replacing a true expression with 1 and a false expression with 0:

%let value   = %eval(FAME > LOVE);       0

%let store   = %sysevalf(5&10);                  1

%let message = %sysevalf(.S|.O|.S);          0

Because "F" is less than "L", the first comparison is false and %EVAL returns 0. Because 5 is true and 10 is also true (0 and missing are the only false numerics), the second statement returns 1. Because all special missing values are false, the third statement returns 0. (Note that | is another way of expressing the word OR.)

Previous examples noted how %IF interprets 1 as true and 0 as false. As with the SAS language, macro language interprets a broad range of numeric values as true or false. %IF interprets integer values only, and 0 is the only false value. Here is a brief macro to demonstrate:

%macro true_false (value);

     %if &value %then %put &value is True.;

     %else %put &value is False.;

%mend true_false;

%true_false (0)      0 is False.

%true_false (3)      3 is True.

%true_false (-5)     -5 is True.

%true_false (1.)     Error, decimal points are not permitted

%true_false ()        Error, no %if condition specified

%true_false (.)       Error, . is not a missing value in macro language

These results have practical implications for common programming statements. Both of these statements can be simplified:

%if %index(&string1, &string2) > 0 %then %do;

%if %length(&list) > 0 %then %do;

Both %INDEX and %LENGTH return nonnegative integers. %IF interprets 0 as false and other integers as true. Thus these replacements are possible:

%if %index(&string1, &string2) %then %do;

%if %length(&list) %then %do;

If the software determines that math should be performed, it will do so even if no functions are present. In these situations, the %EVAL function gets applied automatically to every mathematical expression:

%do i = &a / 2 %to 3 + &b;

%let word5 = %scan(&var_list, 1+4);

The automatic %EVAL saves the clumsiness of having to code:

%do i = %eval(&a / 2) %to %eval(3 + &b);

%let word5 = %scan(&var_list, %eval(1+4));

Note that %EVAL (not %SYSEVALF) is being applied. Therefore, the software performs integer arithmetic.

How does the software “know” when to perform math, even without an explicit %EVAL function? Both the %DO loop and the %SCAN function require numeric values. So the software helps out by performing the math automatically.

Technically, %IF %THEN statements invoke %EVAL every time. So adding %EVAL here is unnecessary:

%if %eval(3 + 2) > 10 %then %do;

But what does that mean in practice? What steps does %EVAL take to evaluate a %IF condition? Begin with a few simpler comparisons:

%if 5 > 10 %then %do; 

%if 3 + 2 > 10 %then %do; 

%if 4 miles < 20 miles %then %do;

These statements are straightforward:

•    The first statement makes a numeric comparison. Because 5 is less than 10, the %IF condition is false.

•    The second statement performs the math and then makes the same numeric comparison.

•    The third statement contains no math. However, there are characters within the comparison. Therefore, the software makes a character comparison. Because “4” is greater than “2”, the comparison is false.

As the expressions become more complex, they reveal details about how the software applies %EVAL to the entire %IF comparison. Consider a few more complex statements:

%if 1 + 3 < 3bc %then %do;

%if 1 + 3 < 5bc %then %do;

%if 1 + 3 miles < 5 miles %then %do;

Hard-coding %EVAL isn’t needed because the software is invoking it anyway. So what do these statements mean?

•    For the first statement, %EVAL notices that there is math to perform. It performs the math, and then it notices there are characters within the comparison. So it performs a character comparison, finds that “4” is greater than “3” and identifies the overall comparison as false.

•    For the second statement, %EVAL again performs the math. The result still contains characters, so a character comparison is made. Because “4” is less than “5”, the comparison is true.

•    In the final statement, %EVAL notices that there is math to perform. However, it attempts to perform math on the entire string “1 + 3 miles”. Because there is no way to perform math on “miles”, this statement generates an error message.

In summary, %EVAL is taking these steps to process a %IF comparison:

•    Inspect each side of the comparison separately. If any mathematical operators appear, apply %EVAL to that entire side.

•    Examine the result. If only integers remain, perform a numeric comparison. But if any non-integers remain, perform a character comparison.

When conducting that initial inspection, mathematical operators (not integers) trigger the software to invoke %EVAL for that side of the comparison. So this is a perfectly valid assignment statement:

%let rank = A-1;

However, this statement always generates an error message:

%if &rank = A-1 %then %do;

The minus sign triggers %EVAL to attempt arithmetic on A-1, generating the error. One way to suppress that attempt is to add double quotes to the comparison. This statement eliminates the error and performs a character comparison:

%if "&rank" = "A-1" %then %do;

But remove the quotes when math should be performed. This group of statements generates Match #1 but not Match #2:

%let a = 3 + 4;

%let b = 4 + 3;

%if &a = &b %then %put Match #1; 

%if "&a" = "&b" %then %put Match #2;

The quotes prevent the math from being performed, resulting in an inequality. But removing the quotes allows %IF conditions to perform math, resulting in equality. Similarly, these statements also produce Match #1 but not Match #2:

%let vision = 20-20;

%let split = 50-50;

%if &vision = &split %then %put Match #1; 

%if "&vision" = "&split" %then %put Match #2;

Finally, this statement performs the math, which is likely not the right action:

%if &lottery_numbers = 1 - 6 - 28 - 39 - 43 - 44 %then %do; 

Other pitfalls exist. In particular, beware of decimal points. Decimal points are so lethal to %EVAL that even this statement would generate an error message:

%do i=1. %to 5.;

With no decimal points, this statement performs a numeric comparison and writes First:

%if 22 + 5 > 4 %then %put First;

However, because of the decimal point, this statement performs a character comparison and writes nothing:

%if 22 + 5 > 4. %then %put Second;

9.2.1 Programming Challenge #7

At last it is time to dig into a macro that is a whopping 20 lines long. This macro (%BUBBLY) reorders a list of four words alphabetically. For example:

%let list = some list of words;

%bubbly (list)

%put &list;   list of some words

%let yoda_speak = this is too clever;

%bubbly (yoda_speak)

%put &yoda_speak;   clever is this too

The macro appears below:

%macro bubbly (macvar);

   %local i j changes dummy

          word1 word2 word3 word4;

   %do i=1 %to 4;

       %let word&i = %scan(&&&macvar,&i,%str( ));

   %end;

   %do %until (&changes=No);

       %let changes=No;

       %do i=1 %to 3;

           %let j=%eval(&i+1); 

           %if &&word&j < &&word&i %then %do;

               %let changes=Yes;

               %let dummy = &&word&i;

               %let word&i = &&word&j;

               %let word&j = &dummy;

           %end;

       %end;

   %end;

   %let &macvar=&word1 &word2 &word3 &word4;

%mend bubbly;

The macro compares the first word to the second, the second word to the third, and the third word to the fourth. Any time the words are not in order, the macro switches them. Finally, if any switches were made, the macro repeats the entire process.

Your mission is to break it using logic, not syntax. Generate an incorrect result, without generating an error message. More specifically, invent a set of four different text strings. But the order of those strings should be unaffected by running the macro, regardless of the order in which they appear.

How is it possible that the macro will fail to switch two different strings regardless of their original order?

9.2.2 Solution

The key is the statement that compares two strings:

%if &&word&j < &&word&i %then %do;

When the %IF comparison contains an arithmetic operator, macro language automatically applies the %EVAL function. So here are two sets of strings that will remain in their original order:

%let list = 3-3 0 5/8 1-1/1;

%let list = 5/8 1-1/1 3-3 0;

The %IF comparison finds equalities every time, never an inequality.

In the classroom, math ranges from simple arithmetic to differential equations and more. You have to pick the level of knowledge that is right for you. Macro language arithmetic similarly can involve more than one level of knowledge.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.137.210.143