To those of you who have not read my previous post, Dividing by zero with SAS, it's not too late to go back and make it up. You missed a lot of fun, deep thought and opportunity to solve an unusual SAS coding challenge.

For those who have already read it, let’s get serious for a second.

When it comes to division by zero in a SAS DATA step, I have found a lot of misconceptions and misunderstandings percolated among SAS online communities and discussion groups. The goal of this post is to dispel all those fallacies and delusions, also known in civilized societies as myths.

## Myth

When SAS encounters division by zero during program execution it generates an **ERROR** message in the SAS log and stops. (A more histrionic version of this myth is, “Your computer disconnects from the Internet and burns down.”)

## Reality

SAS does not generate an **ERROR** message in the SAS log and does not stop executing the current step and subsequent steps in the event of division by zero.

## Myth

When SAS encounters division by zero during program execution it generates a **WARNING** message in the SAS log and continues execution.

## Reality

Consider yourself officially *warned* that SAS does not generate even a **WARNING** message in the SAS log in the event of division by zero.

When SAS encounters division by zero during SAS data step execution it does the following:

1. For each occurrence, it generates a **NOTE** in the SAS log:

`NOTE: Division by zero detected at line XXX column XX.`

2. For each occurrence, it sets the result of division by zero to a missing value and dumps the Program Data Vector (PDV) including data step variables and automatic variables (_N_ and _ERROR_) to the SAS log, e.g.

`a=0 b=0 c=. _ERROR_=1 _N_=1`

3. Finally, it generates a summary **NOTE** to the SAS log, e.g.

`NOTE: Mathematical operations could not be performed at the following places. The results of the operations have been set to missing values.
Each place is given by: (Number of times) at (Line):(Column).
3 at 244:8`

You can run the following SAS code snippet to confirm reality over myth for yourself:

data a; input n d; datalines; 2 0 -2 0 0 0 ; data b; set a; c = n/d; run; |

## Myth

SAS programmers don’t care what messages SAS generates in the SAS log about division by zero and just ignore them.

## Reality

Good programming practices and good SAS programmers do care about the possibility of dividing by zero and try to eliminate those messages by taking control over the situation. For example, instead of blindly dividing by a variable that can potentially have zero values, you can write the following “clean” code:

if d ne 0 then r = n/d; else r = .; |

In this case the division by zero event will never happen during program execution, and you will not receive any nastygrams in the SAS log, but still the result will be the same – a missing value.

## Myth

To avoid those `“Division by zero detected”` NOTEs, one can use **ifn()** function as in the following example:

`r = ifn(d=0, ., n/d);`

The assumption is that SAS will first evaluate the first argument (logical expression d=0); if it is true, then return the second argument (missing value); if false, then evaluate and return the third argument (n/d).

## Reality

Despite the **ifn()** function being one of my favorites, the reality is that it works somewhat differently than the above assumption – *it evaluates all its arguments before* deciding which argument to return. If d does in fact equal 0, evaluating the third argument, n/d, will trigger an attempt to divide by 0, resulting in the “Division by zero detected” NOTE and the PDV dump in the SAS log; that disqualifies this function from being a graceful handler of division by zero events.

## Myth

SAS does not have an effective solution for graceful handling of division by zero events; therefore, SAS programmers are compelled to write additional special programming logic.

## Reality

SAS doesn’t just have an *effective* solution to gracefully handle division by zero events. It has a *perfect* solution! That perfect solution is even conveniently named the divide() function.

The **divide(n,d)** function has two arguments, each is either a numeric constant, variable, or expression. It divides the first argument by the second argument and returns a non-missing quotient in cases when none of the arguments is missing and the second argument is not zero. Otherwise, it returns missing value. No ugly `“Division by zero detected”` NOTES in the SAS log, just what we want.

So, instead of using:

`r = n/d;`

you would just use

`r = divide(n,d);`

Moreover, it gives you extended functionality by providing additional information about its argument’s composition. In the case of a zero divisor, it returns three different types of missing values. If the dividend (the first argument) is positive, it returns a special missing value of .I (I for infinity); if the dividend is negative, it returns special missing value of .M (M for minus infinity); if dividend is zero, it returns . as an ordinary missing value.

## The icing on the cake

SAS’ missing() function equates all those ordinary and special missing values:

`missing(.) = missing(.I) = missing(.M) = 1.`

If for some reason you don’t like having different missing values X in your results after using the `X=divide(n,d)` function, you can easily recode them all to the generic missing value:

`if missing(X) then X=.;`

## Your Comments

How do you prefer handling division by zero? Please share with us.

## 7 Comments

I would consider using a format (e.g. infbest.) to display infinities:

Thank you, Bart, great point! SAS formats are always useful. Knowing that symbol "∞" has Unicode representation of '221E' we can even write user-defined format to use "∞" for "Infinity":

I couldn't agree more!

Nice post Lenny.

Thank you, Jonathan!

Thanks for the overview of how the DATA step behaves. One clarification. the DATA step will display a WARNING when the number of NOTES exceeds a predetermined number. If the DATA step encounters many divisions by zero (20, by default), you will see many notes that look like

NOTE: Division by zero detected at line ...

and then you will eventually see following warning:

WARNING: Limit set by ERRORS= option reached. Further errors of this type will not be printed.

You can see the value of the ERRORS= system option by using

and you can change the value by using the OPTIONS statement.

It is also worth noting that your comments apply to the DATA step, not to all SAS languages and procedures. For example, in SAS/IML, dividing by zero results in a warning:

WARNING: Division by zero, result set to missing value.

In some SAS/STAT procedures that use the CMP subsystem (such as PROC NLMIXED), the procedure will abort execution and stop:

NOTE: Execution error for observation 1.

NOTE: The SAS System stopped processing this step because of errors.

Thank you, Rick, for weighing in. All valid points that make excellent addition to this post. As you suggested, I have added clarification in the third paragraph regarding this post being about the DATA step programming.