UP ONE LEVEL: ENEL 369 Winter 2001 Course Handouts

# ENEL 369: Introduction to Computer Architecture Lab 6 - for the week of February 26

Author: Steve Norman
Paper copies handed out: Monday, February 28, 2001

## This lab will not be marked

In order to allow posting of solutions a few days before the mid-session test, this lab will not be marked. I promise to put at least one question about floating-point numbers on the test; that gives you an incentive to take this week's exercises seriously. (The question will be much less difficult than Exercise D of this lab.)

## Read Section 4.8 of Patterson and Hennessy carefully!

All four exercises assume a solid understanding of floating-point arithmetic. To get such an understanding, you will have to read Section 4.8 of the text very carefully.

## Exercise A: The minifloat type

To understand how floating-point number representation and arithmetic work, it's useful to consider some pencil-and-paper examples. However, doing pencil-and-paper work with 32-bit patterns (or worse, 64-bit patterns) is tedious, so in this exercise and Exercise C we'll do it with an 8-bit floating type called the minifloat type. The minifloat type is intended to mimic the IEEE 754 32-bit and 64-bit floating-point types. With only 8 bits, the precision and range of magnitudes for minifloat will obviously be severely limited. The layout of a minifloat is as follows:

Before continuing to describe the minifloat type, I need to introduce a bit of notation and some terminology. I will use the ** operator (borrowed from Fortran) for exponentiation; this is handy when writing mathematics in Web pages and program comments. (So, for example, 2**4 is 2 to the 4th power.)

I will use the terms hidden-MSB significand and full significand to distinguish between two related concepts. The distinction is most easily shown with an example: consider the number

```  1.1011 * two ** three
```
where 1.1011 is a base two number. The full significand is 1.1011, and the hidden-MSB significand is 0.1011. The word significand in Section 4.8 of Patterson and Hennessy sometimes means hidden-MSB significand and sometimes means full significand; you have to pay careful attention to determine which meaning is in use in a particular sentence or formula.

The sign bit of a minifloat is 0 for positive numbers and 1 for negative numbers.

The exponent field of a minifloat is used as follows:

• Bit patterns 001, 010, 011, 100, 101 and 110 are used for exponents of normal, nonzero numbers. There is a bias of 3 in the exponent.
• The bit pattern 000 is the exponent field in for representations of zero. (IEEE 754 types use all-0 exponents for zero values and also for very-small-magnitude nonzero values called denormalized numbers; we won't bother with denormalized numbers in the minifloat type.)
• The bit pattern 111 is not used at all. (IEEE 754 types use all-1 exponents to indicate special values of Infinity and NaN--not a number; we won't bother with Infinity and NaN in the minifloat type.)

In a nonzero minifloat, the significand bits are used to represent the hidden-MSB significand. In representations of zero, the significand bits must all be 0.

Now that the uses of the individual fields have been described, it's possible to determine what real number (if any) is represented by an 8-bit pattern.

• First of all, both 00000000 and 10000000 represent zero; note that in an IEEE 754 system, there are also two representations of zero.
• Next, there are some bit patterns that don't represent any real number at all. Examples are: 10001010, which has a special exponent of 000 but does not have 0000 in its significand field; and 01110000, which has the meaningless exponent of 111.
• Finally, there are normal, nonzero numbers, such as 01101011. To see what number this is, first split it into fields: the sign bit is 0, the exponent field is 110, and the significand field is 1011. In base two, then, the number represented is
```  1.1011 * two ** three
```
Note that the 5-bit full significand is obtained by putting a 1 in front of the 4-bit MSB-hidden significand and that the exponent of three is obtained by subtracting the bias of three from the value of six (110). In base ten, the number is
```  (1 + 1/2 + 1/8 + 1/16) * 2 ** 3 = 1.6875 * 8 = 13.5
```

Let's consider the problem of determining a minifloat representation of a number given in base 10, say 0.390625. To start with we need to write our number in the form

```  sign * (full significand) * 2 ** exponent
```
where the full significand is greater than or equal to 1 and less than 2. This gives us
```  0.390625 = 1 * 1.5625 * 2 ** (-2)
```
The sign bit should be 0, obviously. The biased exponent should be -2 + 3, or 001 in base two. What is the MSB-hidden significand? Here I will cheat a little and just state that 0.5625 is in fact exactly 1/2 + 1/16, so the correct bit pattern is 1001. Putting all the bit fields together, the minifloat representation is 00011001.

The MSB-hidden significand in a minifloat is

```  bit3 / 2 + bit2 / 4 + bit1 / 8 + bit0 / 16
```
From this it should be clear that the MSB-hidden significand is always an integer multiple of 1/16. Determining the significand bits in the previous example was relatively easy because 0.5625 just happened to be an integer multiple of 1/16. Most base ten fractions are not integer multiples of 1/16, so usually when converting from base ten to minifloat, the fractional part of the full significand will have to be rounded to an integer multiple of 1/16. The following table is useful for doing such rounding:
```VALUE  BASE TWO  BASE TEN  | VALUE  BASE TWO  BASE TEN
0/16   0.0000    0.0000   |  8/16   0.1000    0.5000
1/16   0.0001    0.0625   |  9/16   0.1001    0.5625
2/16   0.0010    0.1250   | 10/16   0.1010    0.6250
3/16   0.0011    0.1875   | 11/16   0.1011    0.6875
4/16   0.0100    0.2500   | 12/16   0.1100    0.7500
5/16   0.0101    0.3125   | 13/16   0.1101    0.8125
6/16   0.0110    0.3750   | 14/16   0.1110    0.8750
7/16   0.0111    0.4375   | 15/16   0.1111    0.9375
```
Let's find a minifloat representation for -3.6, which is
```  -1 * 1.8 * 2**1
```
The sign bit is 1 and the exponent field is 100. The significand bits depend on what rule we choose for rounding. If we use a round-to-nearest rule, the correct choice is 1101, because 0.8 is closer to 0.8125 than it is to 0.7500. With a round-towards-zero rule, the correct choice is 1100. Other rounding rules could be used instead, but in all cases the correct choice will be either 1100 or 1101 So the minifloat representation of -3.6 would be either 11001101 or 11001100; neither representation would be exact.

### What to do

The exercises below are pencil-and-paper exercises.

For each of the following minifloat bit patterns, determine the equivalent base ten number, or explain why no such base ten number can be found.

```   1. 00010010
2. 11010000
3. 10000000
5. 00001000
4. 00111111
```
Convert each of the following base ten numbers to a minifloat representation, or explain why the conversion is not possible. If rounding is necessary, use round-to-nearest logic.
```   6. -13.1
7. 0.2
8. -0.1
9. 11.0
10. -18.5
```

## Exercise B: Int-to-float conversion

The main goal of this exercise is to learn some details of 32-bit IEEE 754 floating-point number representation. You will study some SPIM code that uses integer instructions to set up the correct bits in a floating-point number. The code performs the operation of converting an integer representation to a floating-point representation. On a MIPS processor with a floating-point unit, this is most easily and efficiently done with one cvt.s.w instruction, but it's interesting to study code that performs the operation with integer instructions only, for two main reasons:
• Many processors do not have floating-point units. On these processors, floating-point operations must be performed using integer instructions. It's useful to have some idea of how complicated--and therefore slow--the resulting code is.
• The use of integer instructions to manipulate bit fields provides a lot of insight into the use of the sign, exponent and significand fields within a floating-point number.

On most present-day computers the C float type uses the 32-bit IEEE 754 representation. Since there are only 24 bits in the full significand of a 32-bit IEEE 754 number, most integers with magnitude greater than 2**24 can't be represented exactly in the float type. For example, if the following program:

```#include <stdio.h>
int main(void)
{
int i = 1234512345, j;
float f;
f = (float) i;
j = (int) f;
printf("i = %d, f = %f, j = %d.\n", i, f, j);
return 0;
}
```
is compiled and run on an x86-based Linux system, the output is
```i = 1234512345, f = 1234512384.000000, j = 1234512384.
```
The conversion on this system picks the nearest float to 1234512345, which is 1234512384.0. (The results are likely to be the same on any other system that has 32-bit ints and 32-bit IEEE 754 floats.) Picking the nearest float is a reasonable choice, but is not required by the C standard, which says that the result of an inexact int-to-float conversion must be one of two numbers: the smallest float larger than the int or the largest float smaller than the int.

### What to do

Copy the directory /local/courses/enel369/lab6/exB

Study the code and comments in int2float.spim. Make a printout and trace the program instruction by instruction, using a pen or pencil to keep track of the contents of registers used in the procedure int2float. What are the contents of \$a0, \$t0, and \$t1 at point one? What is \$v0 at point two? Make a serious effort to get correct answers without running the program; this will take some time but it should teach you quite a bit about manipulating bit patterns with logic instructions such as sll, srl, and, and or.

Use xspim to run the program. If you set a breakpoint or two in appropriate places you can very quickly check the values you found when tracing the program by hand.

## Exercise C: Multiplication of minifloats

This exercise is designed to help you understand the steps required to multiply two floating-point numbers, in a way that should provide some hints about how to do Exercise D.

Suppose you are asked to find the product of two minifloats, and the only tools you have are a pencil and some paper.

Here is an algorithm that works on positive minifloats only, illustrated with an example. Let's suppose you are asked to multiply 01101011 and 00101101.

• First, extract the exponents: 110 and 010. Because of the bias, the exponents are 3 (6 - 3) and -1 (2 - 3). The sum of the exponents is 2, so you can tentatively set the exponent of the product to be 101 (2 + 3).
• Second, find the two full significands by putting 1's in front of the 4-bit hidden-MSB sigificands. For the example, 1.1011 (base two) and 1.1101 (base two) are the full significands.
• Third, multiply the full significands. Because they are both greater than or equal to one and less than two, the result will be greater than or equal to one and less than four. In our example, the multiplication produces 11.00001111 (base two) , which is just a little larger than three.
• Fourth, determine the significand for the overall result. This needs to be greater than or equal to one and less than two. You can achieve that by dividing 11.00001111 by two to get 1.100001111 and incrementing the exponent of the result up by one from 101 to 110. (Dividing the result by two and incrementing the exponent would be skipped in a case where the product of significands is less than two.) Rounded to five bits, the full significand is 1.1000; therefore the hidden-MSB significand bits will be 1000.
• Combine the sign, exponent, and hidden-MSB significand to get an overall result, which for the example would be 01101000.

### What to do

Use the above algorithm to compute
• the product of minifloats 00110010 and 00100000;
• the product of minifloats 00111000 and 00111000.

## Exercise D: 32-bit floating-point multiplication with integer instructions

Use of the mul.s instruction is the right way to multiply two 32-bit floating-point numbers on a MIPS processor with a floating-point unit.

On processors without a floating-point unit, a sequence of integer instructions is needed to multiply two 32-bit floating-point numbers. You can learn a lot about floating-point arithmetic by attempting to multiply floating-point numbers with integer instructions.

### What to do

Copy the directory /local/courses/enel369/lab6/exD

Run the program floatmul.spim to see what it does.

Add code to the floatmul procedure so that it does the job described in the comments near the top of the procedure. In order to keep this exercise relatively simple, you may make the following assumptions:

• The numbers sent in arguments \$a0 and \$a1 are not +Infinity, -Infinity, NaN, or denormalized.
• The procedure does not have to produce a correct result if the product of \$a0 and \$a1 is too large or too small in magnitude to represent in the IEEE 754 32-bit format.
• The significand of the result may be computed by rounding towards zero, which is easier than rounding to the nearest float.
There are two main issues in designing your routine:
1. You have to manipulate bit patterns to isolate signs, exponents and significands.
2. You have to find a way to multiply two 24-bit full significands with the multu instruction and get a 24-bit full significand of out of the Hi register. Here's one way to do it: put the full significand of \$a0 in bits 31-8 of \$t0 and the full significand of \$a1 in bits 31-8 of \$t1; after doing a multu, the 24-bit full significand of the product will be in either bits 31-8 or bits 30-7 of the Hi register, depending on whether the product of significands is less than or greater than two.

This is a relatively challenging exercise. Make a serious effort to get your code working, but recognize that the main goal is to learn about floating-point number representations and manipulation of bit fields. (In other words, don't burn twelve hours trying to debug code that almost works!)