You probably know about the *w.d* and BEST*w*. formats. They have been around throughout my entire 38-year history of using SAS. Do you also know about the D*w.p* and BESTD*w.p* formats? You might find that they are the best D formats around and are even better than BEST! The D*w.p*. and BESTD*w.p* formats work well when the range of values is sufficiently large that different values need to be formatted by using different *w.d* formats. Specifying BESTD*w.p* (where *p* = *w* - 1) is often better than specifying BEST*w.* for columns of numbers, since the decimal does not shift when the last digit is 0.

d10.1 d10.3 d10.6 d10.9 best10. bestd10.9 10.5 2.06115E-9 2.06115E-9 2.06115E-9 2.06115E-9 2.06115E-9 2.06115E-9 0.00000 5.6028E-9 5.6028E-9 5.6028E-9 5.6028E-9 5.6028E-9 5.6028E-9 0.00000 1.523E-8 1.523E-8 1.523E-8 1.523E-8 1.523E-8 1.523E-8 0.00000 0.0000008 8.31529E-7 8.31529E-7 8.31529E-7 8.31529E-7 8.31529E-7 0.00000 0.0000167 0.0000167 0.00001670 0.00001670 0.0000167 0.00001670 0.00002 0.0000454 0.0000454 0.00004540 0.00004540 0.0000454 0.00004540 0.00005 0.0001 0.0001234 0.00012341 0.00012341 0.00012341 0.00012341 0.00012 0.0498 0.04979 0.0497871 0.04978707 0.04978707 0.04978707 0.04979 1.0000 1.00000 1.000000 1.00000000 1 1 1.00000 2.7183 2.71828 2.718282 2.71828183 2.71828183 2.71828183 2.71828 7.3891 7.38906 7.389056 7.38905610 7.3890561 7.38905610 7.38906 54.5982 54.59815 54.598150 54.5981500 54.59815 54.5981500 54.59815 403.4288 403.42879 403.4288 403.428793 403.428793 403.428793 403.42879 8103.0839 8103 8103.0839 8103.08393 8103.08393 8103.08393 8103.08393 22026 22026 22026.47 22026.4658 22026.4658 22026.4658 22026.4658 24154953 24154953 24154953 24154952.8 24154952.8 24154952.8 24154952.8 65659969 65659969 65659969 65659969.1 65659969.1 65659969.1 65659969.1 |

Here is a review of some of the basic formats for displaying numbers.

** w.d** displays a numeric value in a field

*w*positions wide, with

*d*positions to the right of the decimal place, and with

*w*-

*d*- 1 positions to the left of the decimal place. Use it when you know the range of value (such as correlation coefficients) or when you know the typical range (such as

*t*statistics).

Example:

x = 100 / 3; y = -9.9999; z = -12.3333333; put x 7.3 / y 7.4 / z 7.4; 33.333 -9.9999 -12.333 |

Notice that the decimal automatically shifts for extreme values.

**BEST w.** displays numeric values, in a field

*w*positions wide, using the best format for each particular value. Decimals might not align for many of the values. This can make results difficult to read.

Example:

x1 = 100 / 3; x2 = x1 ** 2; x3 = 1 / x1; x4 = x1 / 10; x5 = 12; format x1-x5 BEST8.; put (x1-x5) (/); 33.33333 1111.111 0.03 3.333333 12 |

**D w.p** displays numeric values, in a field

*w*positions wide, possibly with a great range of values, lining up decimals for values of similar magnitude. The precision parameter is

*p*. The D

*w.p*format selects a

*w.d*(or possibly E

*w.*) format for each value, with the number of decimal points

*d*chosen so that values of similar magnitude are displayed with decimal points that line up within a column of values. For a given field width

*w*, this internal selection of

*w.d*formats is made subject to the precision constraint specified by

*p*. That is, every value will be displayed with at least

*p*significant digits shown (counting digits both to the left and the right of the decimal point).

Example:

x1 = 100 / 3; x2 = x1 ** 2; x3 = 1 / x1; x4 = x1 / 10; x5 = 12; format x1-x5 D8.; put (x1-x5) (/); 33.3333 1111.1 0.0300 3.3333 12.0000 |

**BESTD w.p** displays numeric values, in a field

*w*positions wide, lining up decimals for values of similar magnitude, and displays integers without decimals. As the name implies, this format combines the BEST

*w.*format for integers and the D

*w.p*format for nonintegers.

Example of BestD8., which uses the default *p* = 3:

x1 = 100 / 3; x2 = x1 ** 2; x3 = 1 / x1; x4 = x1 / 10; x5 = 12; format x1-x5 BestD8.; put (x1-x5) (/); 33.3333 1111.1 0.0300 3.3333 12 |

Example of BestD8.7:

x1 = 100 / 3; x2 = x1 ** 2; x3 = 1 / x1; x4 = x1 / 10; x5 = 12; format x1-x5 BestD8.7; put (x1-x5) (/); 33.33333 1111.111 0.030000 3.333333 12 |

Specifying BESTD*w.p* (where *p* = *w* - 1) is better than specifying BEST*w.* for columns, since decimals do not shift when the last digit is 0.

Example:

proc iml; x = {-2 -1.2 -.52 0 .5 1.3 1.5 2.4 3 3.5 4 4.5 5 6 7 8}; knots = {-7 -4 -2 0 5 8 12 17}; b = bspline(x,4,knots); print b[format=bestd7.6 label='BestD7.6'] ' ' b[format=best7. label='Best7.']; quit; BestD7.6 Best7. 0.16534 0.59550 0.23099 0.00816 0 0.16534 0.5955 0.23099 0.00816 0 0.16534 0.59550 0.23099 0.00816 0 0.16534 0.5955 0.23099 0.00816 0 0.16534 0.59550 0.23099 0.00816 0 0.16534 0.5955 0.23099 0.00816 0 0.16534 0.59550 0.23099 0.00816 0 0.16534 0.5955 0.23099 0.00816 0 0.10848 0.55622 0.31543 0.01986 7.66E-6 0.10848 0.55622 0.31543 0.01986 7.66E-6 0.04958 0.45179 0.44110 0.05718 0.00035 0.04958 0.45179 0.4411 0.05718 0.00035 0.03970 0.42132 0.46769 0.07067 0.00062 0.0397 0.42132 0.46769 0.07067 0.00062 0.01209 0.28103 0.55021 0.15260 0.00407 0.01209 0.28103 0.55021 0.1526 0.00407 0.00423 0.19585 0.56541 0.22458 0.00993 0.00423 0.19585 0.56541 0.22458 0.00993 0.00134 0.13569 0.55242 0.29216 0.01839 0.00134 0.13569 0.55242 0.29216 0.01839 0.00026 0.08757 0.51761 0.36319 0.03137 0.00026 0.08757 0.51761 0.36319 0.03137 0.00002 0.05202 0.46436 0.43334 0.05025 0.00002 0.05202 0.46436 0.43334 0.05025 0 0.02813 0.39792 0.49737 0.07659 0 0.02813 0.39792 0.49737 0.07659 0 0.02813 0.39792 0.49737 0.07659 0 0.02813 0.39792 0.49737 0.07659 0 0.02813 0.39792 0.49737 0.07659 0 0.02813 0.39792 0.49737 0.07659 0 0.02813 0.39792 0.49737 0.07659 0 0.02813 0.39792 0.49737 0.07659 |

**D w.p Format Details.** D

*w.p*does not imply

*w.d*. The

*p*specification is a precision specification. It is not a number-of-decimal-places (

*d*) specification. First, note that the effective values for

*p*range from 1 to 9. D

*w*. (no

*p*), D

*w*.0, and D

*w*.3 all mean the same thing:

*p*=3. Here is a column of numbers formatted 6 different ways, illustrating various choices of

*p*:

d10.1 d10.2 d10.3 d10.5 d10.7 d10.9 2.06115E-9 2.06115E-9 2.06115E-9 2.06115E-9 2.06115E-9 2.06115E-9 0.0000061 0.0000061 6.14421E-6 6.14421E-6 6.14421E-6 6.14421E-6 0.0000167 0.0000167 0.0000167 0.00001670 0.00001670 0.00001670 7.3891 7.3891 7.38906 7.389056 7.3890561 7.38905610 20.0855 20.0855 20.08554 20.085537 20.085537 20.0855369 54.5982 54.5982 54.59815 54.598150 54.598150 54.5981500 148.4132 148.4132 148.41316 148.413 148.41316 148.413159 403.4288 403.4288 403.42879 403.429 403.42879 403.428793 1096.6332 1096.6332 1097 1096.633 1096.6332 1096.63316 8103.0839 8103.0839 8103 8103.084 8103.0839 8103.08393 22026 22026 22026 22026.466 22026.466 22026.4658 59874 59874 59874 59874.142 59874.142 59874.1417 162755 162755 162755 162755 162754.79 162754.791 442413 442413 442413 442413 442413.39 442413.392 1202604 1202604 1202604 1202604 1202604.3 1202604.28 8886111 8886111 8886111 8886111 8886110.5 8886110.52 24154953 24154953 24154953 24154953 24154953 24154952.8 65659969 65659969 65659969 65659969 65659969 65659969.1 178482301 178482301 178482301 178482301 178482301 178482301 485165195 485165195 485165195 485165195 485165195 485165195 |

Some of the results are as follows:

**Display at least 1 significant digit.**When*p*= 1 (the minimum) , there are relatively few groups of values and decimals that align within those groups. Hence decimals appear in fewer of the*w*positions, and as a result, some values might be displayed with relatively less precision. In this case, there is one group of values that is displayed with the E*w*. format and three additional groups (decimals in positions 3, 6, and not displayed).**Display at least 2 significant digits.**When*p*= 2, there tend to be more ranges of values where decimals align. Hence, decimals might appear in more of the*w*positions, and values tend to be displayed with more precision. In this case, there is one group of values that is displayed with the E*w*. format and three additional groups (decimals in positions 3, 6, and not displayed).**Display at least 3 significant digits.**When*p*= 3 (the default), there will tend to be still more ranges of values where decimals align. Hence, decimals might appear in still more of the*w*positions, and values tend to be displayed with still more precision. In this case, there is one group of values that is displayed with the E*w*. format and three additional groups (decimals in positions 3, 5, and not displayed). Notice that some values are displayed with more precision than with smaller values of*p*.**Display at least 9 significant digits.**When*p*= 9 (the maximum), there will tend to be still more ranges of values where decimals align. Hence, decimals can appear in all but the first and last of the*w*positions, and values will be displayed with maximum precision. Decimals will not align for many values. In this case, there is one group of values that is displayed with the E*w*. format and eight additional groups (decimals in positions 2, 8 and not displayed). Notice that most values are displayed with more precision than with smaller values of*p*.

As *p* increases, more different *w.d* formats are likely to get chosen, values tend to be displayed with more precision, and decimals are less likely to align. The choice of *p* and *w* are related. If you want to ensure more worst-case precision, then a wider field width is appropriate. Also, one advantage of D*w.d* is that it allows more moderate width columns. If all you have is *w.d*, and you want to ensure reasonable precision for values of different scales, then you are pretty much forced to use very wide columns with a large number of decimal places and hope for the best.

Submit the following steps to see more values that are formatted using a variety of D and other formats. The table of formatted numbers displayed above is a subset of the results displayed by these steps.

data x(drop=i j l f); array d[13] d0-d12; length l f $ 200; do j = 0 to 9; l = catx(' ', l, cats('d', j), '=', cats('"d10.',j,'"')); f = catx(' ', f, cats('d', j), cats('d10.',j)); end; l = catx(' ', 'label', l, 'd10 = "best10." d11 = "bestd10.9" d12 = "10.5";'); f = catx(' ', 'format', f, 'd10 best10. d11 bestd10.9 d12 10.5;'); call symputx('lf', catx(' ', l, f)); do i = -20 to 20; d0 = exp(i); do j = 2 to 13; d[j] = d0; end; output; end; run; options ls=142; proc print noobs label; &lf run; |

**Letting ODS pick the format.** In an ODS table template, you can use the CHOOSE_FORMAT= option to choose a format. Use it with the FORMAT_WIDTH=*w* option to choose a good format for the specified width (*w*).

**CHOOSE_FORMAT=MAX_ABS**

picks a good format for a single number based on the maximum absolute value. This is the simplest case.

**CHOOSE_FORMAT=MAX**

picks a good format for a single number based on the maximum. Values are all expected to be nonnegative so no space is reserved for a minus sign.

**CHOOSE_FORMAT=MIN_MAX**

picks a good format for a single number based on the minimum and the maximum. Values can be mixed, positive and negative. This option provides room for a minus sign only where it is actually needed. So if all values happen to be positive, no room is reserved for a minus sign. If values range from say -5 to 10, no extra column is needed for the minus sign. If you also specify FORMAT_NDEC=*ndec* in PROC TEMPLATE, *ndec* provides a ceiling on the maximum number of decimal places.

**CHOOSE_FORMAT=COMPROMISE**

picks a good *w.d* format for a column of numbers if they are not too heterogeneous.

**History.** The *w.d* and BEST*w.* formats have been around for decades. Many SAS formats were written by Rick Langston, although the original mainframe *w.d* and BEST*w.* formats precede Rick. The functionality that underlies the D*w.p* format has also been around for decades, but it has not always been available as a format. Mark Little wrote it, and for many years it was only available for procedures to use internally. When SAS started making output using ODS, then that functionality was surfaced as the D*w.p* format. The BESTD*w.p* format came much later.

**Conclusions.** When you explicitly specify a format, you typically specify *w.d* when the values have a known range (such as correlation coefficients) or D*w.p* when values are more diverse. You might instead use BESTD*w.p* when you expect a mix of integers and nonintegers. BEST*w.* is not the best format for columns; it is best when used for scalars. E*w*. is useful for large or small numbers or when extra precision is desired. D*w.p*. and BESTD*w.p* work well when the range of values is large. Experiment with the precision parameter *p* to get the best display of your results. In particular, setting *p* to *w* - 1 and specifying BESTD*w.p* provides a good alternative to the BEST*w.* format.