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
Last modified: Tue Feb 20 16:13:59 MST 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.)

Solutions will be posted on the Web by Friday, Feb. 26. Follow the links on the course Home Page.

[back to top of document]

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.

[back to top of document]

Exercise A: The minifloat type

Read This First

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:
minifloat bit layout

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:

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.

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:
 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

[back to top of document]

Exercise B: Int-to-float conversion

Read This First

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:

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.

[back to top of document]

Exercise C: Multiplication of minifloats

Read This First

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.

What to do

Use the above algorithm to compute

[back to top of document]

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

Read This First

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:

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!)

[back to top of document]