# Formatting and logical statments

## Formatting - using 'fprintf'

In scientific computing, the manner in which we format output (tables of numbers for example) depends on what we plan to do with those numbers. If all you want to see is whether a number is positive or negative, you might only need very few digits. But if you want to see how how many digits of accuracy you get in a particular approximation, you might want the full 16 digits available.

For this reason, it is useful to have a flexible way of presenting numerical output. We have already learned the format statement. Now we are going to learn a more sophisticated way of printing numbers to the screen or to a file. The fprintf command is particularly useful. It does not take arguments in the traditional sense, but instead uses "format strings" to indicate how text and numbers should be printed.

Here is how we might use fprintf to print a table of numbers. In this example, we print the same number in several different ways.

x = -2e2 + 4e2*rand(1,5);  % Numbers in [-200,200]
fprintf('XX|XXXXX.XX|XXXXXXX.XXXXXXXX|XXX.XXXXXXXXXXXXXXXXe+NN|\n');
for i = 1:5,
fprintf('%2d|%8.2f|%16.8f|%24.16e|\n',i,x(i), x(i),x(i));
end;
XX|XXXXX.XX|XXXXXXX.XXXXXXXX|XXX.XXXXXXXXXXXXXXXXe+NN|
1|  150.15|    150.14863944|  1.5014863944167394e+02|
2|    7.22|      7.22084334|  7.2208433444416755e+00|
3|  177.45|    177.44904982|  1.7744904981935514e+02|
4|   55.08|     55.08363923|  5.5083639228869600e+01|
5|  183.08|    183.07757594|  1.8307757593663331e+02|


The first argument to fprintf in this example is a string. This string contains four format strings, e.g. '%2d', '%8.2f', '%16.8f' and '%24.16e'. The first value in the format string is the field width and the second value is the number of digits after the decimal place to display. The field width is exactly the number of characters between the vertical bars ('|'), including any the decimal point, a possible '-' or '+' (a minus or plus sign) and any characters needed to represent floating point numbers (e.g. the 'e+02' in the above example). The Xs at the top of the table are here to indicate the field width.

In the above example, the format strings 'd','f', and 'e' are used. These are the most common, and are to be used with integers, fixed point formatting, and floating point (scietific notation) formatting, respectively.

Notice what can happen if we don't format properly. In following examples, we show several formatting "mistakes", and a way to correct them.

Example 1 : We are using a fixed point notation, but are not plotting enough digits to see that our number is not zero.

a = 1.456e-10;
fprintf('|XXXXXXXXXX|\n');
fprintf('|%10.4f|\n',a);      % Using 'f' in this case only shows zero digits
fprintf('|%10.3e|\n',a);      % Show non-zero digits
|XXXXXXXXXX|
|    0.0000|
| 1.456e-10|


Example 2 : Here, we have again not specified enough digits, and the print statement is rounding up to the nearest value that can be printed using the format string we have specified.

b = 4.0098734;
fprintf('|XXXXXXXXXX|\n');
fprintf('|%10.2f|\n',b);    % Not wrong, but just be aware that fprintf rounds up
|XXXXXXXXXX|
|      4.01|


Example 3 : In this case, we are using the integer format string 'd', but our number is not an integer. The fprintf statement then reverts to a default format %12.6e.

c = 7.631;
fprintf('|XXXXXXXXXX|\n');
fprintf('|%10d|\n',c);    % The integer format string ('d') is over-ridden.
fprintf('|%10.3f|\n',c);  % Use the 'f' format string instead.
|XXXXXXXXXX|
|7.631000e+00|
|     7.631|


Example 4 : And when we do not specify a field width sufficiently wide to contain the number of digits that should be printed out, the specified field width is overrun.

d = -456745e8;
fprintf('|XXXXXXXXXX|\n');
fprintf('|%10f|\n',d);     % Field width too small; decimal digits are shown
fprintf('|XXXXXXXXXXXXXXX|\n');
fprintf('|%15.0f|\n',d);   % Increase field width; don't show decimal digits
|XXXXXXXXXX|
|-45674500000000.000000|
|XXXXXXXXXXXXXXX|
|-45674500000000|


Example 5 : Finally, if we fail to take into account the width needed for the extra characters involved in the formatting of a floating point number, we also overrun the specified field width.

e = pi*1e-4;
fprintf('|XXXXXXXXXX|\n');
fprintf('|%10.9e|\n',e);      % Field width not large enough
fprintf('|XXXXXXXXXXXXXXX|\n');
fprintf('|%15.9e|\n',e);      % Increase size of field.
|XXXXXXXXXX|
|3.141592654e-04|
|XXXXXXXXXXXXXXX|
|3.141592654e-04|


The command fprintf also works on arrays. In this case, the format string is applied to each entry of the array.

x = -1 + 2*rand(1,5);
fprintf('|XXXXXXXX|\n');
fprintf('|%8.4f|\n',x);      % x is an array
|XXXXXXXX|
| -0.5186|
|  0.3522|
| -0.4219|
|  0.3436|
|  0.3903|


One final formatting tip is the use of the "string" formatting to list a set of values in a nice way. Here is a simple example.

fprintf('%16s %10.1f\n','Temperature (K)',301.3);
fprintf('%16s %10.2e\n','Energy (J)',1.24e6);
fprintf('%16s %10.2f\n','Density (kg/m3)',1.21);
fprintf('%16s %10.2f\n','Pressure (bar)',2.13);
 Temperature (K)      301.3
Energy (J)   1.24e+06
Density (kg/m3)       1.21
Pressure (bar)       2.13


By using the string format string 's', we can right justify the label for each value, which leads to a nicely formatted table.

Back to the top

## Logical operators

Another key concept in programming is the ability to test a conditional statement and make decisions about the flow of the program based on the truth value of the statement. Examples of such statements are "Is A equal to B?" or "Is A less B?" We can also ask compound conditionals such as "Is A < B and C < B?" Or "Is A > B or C > B?".

To formulate the above questions, we will use binary operators that take two arguments ('A' and 'B') in the above examples and returns a value of "true" or "false". In Matlab, "true" is integer 1 and "false" is the integer 0. The binary operators that we will find use in comparing numeric values are called "relational operators", and are given here.

>> help relop
Relational operators.
< > Relational operators.
The six relational operators are <, <=, >, >=, ==, and ~=.
A < B does element by element comparisons between A and B
and returns a matrix of the same size with elements set to logical
1 (TRUE) where the relation is true and elements set to logical 0
(FALSE) where it is not.  A and B must have the same dimensions
(or one can be a scalar).
.................
&   Element-wise Logical AND.
A & B is a matrix whose elements are logical 1 (TRUE) where both A
and B have non-zero elements, and logical 0 (FALSE) where either has
a zero element.  A and B must have the same dimensions (or one can
be a scalar).
................
|   Element-wise Logical OR.
A | B is a matrix whose elements are logical 1 (TRUE) where either
A or B has a non-zero element, and logical 0 (FALSE) where both have
zero elements.  A and B must have the same dimensions (or one can
be a scalar).
...............
~   Logical complement (NOT).
~A is a matrix whose elements are logical 1 (TRUE) where A has zero
elements, and logical 0 (FALSE) where A has non-zero elements.
...............
xor Exclusive OR.
xor(A,B) is logical 1 (TRUE) where either A or B, but not both, is
non-zero.  See XOR.


Here are some simple examples illlustrating the use of relational (or "logical") operators.

true
ans =

1


false
ans =

0


x = 5;
disp(x > 6)
     0


disp(x < 10 & x < 6)
     1


disp(x > 0 | x < -1);
     1


disp(xor(x < 10,x > -1));
     0


disp(xor(x < 10, x < 0));
     1


disp(x < 10 | x < 20);
     1


disp(x > 10 | false);
     0


disp(x < 10 & true);
     1



Back to the top

## Using logical operators with arrays

These relational tests, when used with Matlab arrays, produce another array whose entries are '0' where the relational test is false, and '1' where the relational statement is true. For example,

format short
x = -1 + 2*rand(1,7)  % Random numbers between -1 and 1.
x =

-0.8640   -0.4904   -0.5519    0.3357    0.6888   -0.3111    0.5610


x > 0
ans =

0     0     0     1     1     0     1


x < 0
ans =

1     1     1     0     0     1     0



These 0-1 arrays in turn can be used to index into the original array and mask certain elements. For example, we can pull out only the positive entries of x :

m = x > 0;
x(m)
ans =

0.3357    0.6888    0.5610



Or, we can pull out only the negative entries of x :

m = x < 0;
x(m)
ans =

-0.8640   -0.4904   -0.5519   -0.3111



We can use this array to selectively set entries in x to different values depending on the masking array.

m = x > 0;
x(m) = 10;

m = x < 0;
x(m) = -10;

disp(x)
   -10   -10   -10    10    10   -10    10



We can shorten the above by simply writing

x = -1 + 2*rand(1,7);
y = 10*(x > 0) + -10*(x < 0);
fprintf('%8.2f',x);
fprintf('\n');
fprintf('%8.2f',y);
fprintf('\n');
    0.35   -0.99    0.20   -0.23    0.83   -1.00   -0.08
10.00  -10.00   10.00  -10.00   10.00  -10.00  -10.00


Back to the top

## Conditional statements

Where these logical statements become useful is when they can be used to control the flow of a program. For example, suppose you wanted to divide one number by another number. You might first check to see that the divisor is not equal to 0.

a = 5;
b = 0;
if (b ~= 0)
c = a/b;
else
fprintf('Cannot divide by 0\n');
end;
Cannot divide by 0


A more complex if-else statement can be written like this. In this example, we find two random numbers between -1 and 1. We then check to see if either number is in [-1/3, 1/3] and if so, set the value to 0. We then use if-else statements to find whether a, b or both are positive, negative or equal to 0.

% A rather long way to find the sign of two numbers... (:-))
a = -1 + 2*rand(1,1);
b = -1 + 2*rand(1,1);
a(abs(a) < 1/3) = 0;   % a or b will be zero 1/3 of the time.
b(abs(b) < 1/3) = 0;
if (a*b < 0)
% Either a < 0 or b < 0, but not both
if (a < 0)
fprintf('a < 0\n');
fprintf('b > 0\n');
else
fprintf('a > 0\n');
fprintf('b < 0\n');
end
elseif (a*b > 0)
% Both a > 0 and b > 0, or a < 0 and b < 0
if (a < 0)
fprintf('a < 0\n');
fprintf('b < 0\n');
else
fprintf('a > 0\n');
fprintf('b > 0\n');
end;
else
% Either a == 0, b == 0 or both.
if (xor(a == 0, b == 0))
if (a ~= 0)
if (a > 0)
fprintf('a > 0\n');
else
fprintf('a < 0\n');
end;
fprintf('b == 0\n');
else
fprintf('a == 0\n');
if (b > 0)
fprintf('b > 0\n');
else
fprintf('b < 0\n');
end;
end;
else
fprintf('a == 0\n');
fprintf('b == 0\n');
end;
end;
fprintf('a = %f\n',a);
fprintf('b = %f\n',b);
a == 0
b == 0
a = 0.000000
b = 0.000000


Notice that we did not include any statements checking whether x was exactly equal to another number. The following demonstrates why such a test might not always give you what you expect.

x = cos(pi/3);
if (x == 0.5)
fprintf('cos(pi/3) == 0.5\n');
else
fprintf('cos(pi/3) is not exactly 0.5!\n');
end;

fprintf('cos(pi/3) is very close to 0.5, but not exactly 0.5\n');
fprintf('abs(cos(pi/3) - 0.5) = %16.8e\n',abs(x-0.5));
cos(pi/3) is not exactly 0.5!
cos(pi/3) is very close to 0.5, but not exactly 0.5
abs(cos(pi/3) - 0.5) =   1.11022302e-16


Back to the top

## Using arrays in conditional statements

We can also use conditional statements in arrays. In this case, we have to come up with a single truth value for the logical array, and as a result, we must be very careful about how we use logical arrays in conditional statements. For example, consider the following code fragment :

x = -1 + 2*rand(1,5);     % 1x5 array of numbers in [-1,1]
if (x < 0)
x = -10;
else
x = 10;
end;
disp(x)
    10



You might think that this sets negative entries in x to -10 and positive entries to 10. But in fact, all it does is set the array x to the scalar value 10. Why? The first statement x < 0 will only be true if all entries of x are negative. Since we probably have a mix of both positive and negative entries, the code will set x to the scalar value 10.

Back to the top

## Lab exercise

In this lab, you will practice using the fprintf statements, and using conditional statements.

### Exercise #1

Use the fprintf to print out the following numbers.

a = 5
b = 14.567
c = exp(12)
d = 2^(-12)


Your output should look exactly like the second line in the output displayed below :

|XXXXX|XXXXXXXXXX|XXXXXXXXXXXX|XXXXXXXXXXXXXX|
|    5|    14.567|  1.6275e+05|3.05175781e-05|


The 'X's are placed as a guide and don't need to be printed. Count the field width of the formatted string to make sure you have correctly spaced the digits before and after the decimal, and any additional characters needed for scientific notation.

### Exercise #2

Plot the following discontinuous function over the interval [-5,5]. Be sure to include any end point conditions.

Back to the top