CPSC 220, Fall 2022
Lab 1: Numbers and Bits
Welcome to the first lab for CPSC 220. In this lab, you will implement conversion between values of type int and their string representations in decimal, binary, and hexadecimal form. You will also convert an int value into the float value that is represented by exactly the same bits. You will be working in Java. Of course, Java can do all of these conversions itself, but for this lab, you will have to program them yourself using only basic arithmetic and bitwise operations.
You will start with an incomplete program, Bits.java. This program is meant to let the user enter a 32-bit integer in any of the following forms: an ordinary base-10 integer, a binary number starting with "0b", a hexadecimal number starting with "0x", or a floating point number with a decimal point or exponent. It will print out the value in all four formats. The program already exists in outline and can be run.
The assignment is to complete the program Bits.java, as discussed below. You should turn in your completed program by the start of next week's lab.
Turning in Your Work
You have an account on the Math/CS Department's Linux computers. I will make sure that you have the username and password that you need to use the account. Note that you are not required to work on the lab in Linux, but you are required to turn your lab work in to your Linux account. You will submit your work by copying it into a homework folder in that account. The name of the folder is /classes/cs220/homework/LastName, where "LastName" should be replaced by your actual last name.
For this lab, you will submit one file, named Bits.java. One way to do that from any computer that has a network connection is to use the scp command in a Terminal. Change into the directory that contains Bits.java and give a command of the form
scp Bits.java zz9999@math.hws.edu:/classes/cs220/homework/LastName
where you have to replace zz9999 with your username and replace LastName with your actual last named. If you are working on one of our Linux computers, you can use
cp Bits.java /classes/cs220/homework/LastName
(Note that it's also possible to use an SFTP program such as FireZilla Client for transferring files between your computer and your Linux account. And if you are working on a Linux computer in the lab, you can use the GUI to copy Bits.java into your homework directory.)
If you have not managed to submit your file by the start of next week's lab, you can get help to submit it during lab. You should also be able to get help from Teaching Fellows; their hours are Sunday through Thursday, 7:00 to 10:00 PM, starting Sunday, August 28.
About the Conversions
Bits.java contains seven subroutines for converting between different number representations. One of them is already complete. Your only job for this lab is to complete the other six conversion functions. You must do the conversions without using any of Java's built-in subroutines. Furthermore, your subroutines should never crash the program; they should detect any errors in the input and throw an IllegalArgumentException for any error that is found.
The function floatStringToInt() is already complete. It uses Java's built-in Float.parseFloat() to convert an input string to the floating point value that it represents. The result is a 32-bit binary representation of the float value. The subroutine has to return the int value that is represented by the same 32 bits. The conversion is done using Float.floatToRawIntBits(). This function does not actually do anything with its input; it just returns the unmodified 32 bits, but treated as an int instead of as a float. I don't know of any other way to do this type conversion in Java.
The reverse conversion from int to float could be done by Float.intBitsToFloat(). However, for this lab you are asked to implement the same conversion in the function myIntBitsToFloat(). The algorithm is given in the documentation for Float.intBitsToFloat(). You should implement that algorithms exactly. One note on the implementation: When Java computes s*m*Math.pow(2.0,e-150), the result is of type double, and you will have to type-cast it to type float.
For the remaining String conversion functions, hexStringToFloat(), binaryStringToFloat(), and decimalStringToFloat(), you should apply the basic algorithm for converting a base-N string to an integer:
num = 0 for each character, ch, in the string num = N*num + (base-N equivalent of the character ch)
There are a few issues that you will have to deal with: You will want to throw IllegalArgumentException for illegal characters and values that are too big to be represented in 32-bits. To make it possible to check for out-of-range values, you are strongly encouraged to use type long in your calculations, and type-cast to type int at the end. Binary and hexadecimal strings are meant to be unsigned, allowing values in the range 0 to 232-1. For decimal numbers, you will need to account for the fact that a decimal string is allowed to start with a plus or minus sign, and you can allow values from –231 to 232-1.
Finally, you need to complete intToBinaryString() and intToHexString(). You need to produce the base-2 and base-16 strings that represent a 32-bit integer value, n. To do that, you need to look at the individual bits or groups of four bits in n. For this lab, you are required to do that using Java's bitwise operators. (I don't care whether you include leading zeros in your strings. For example, both 0x9fa3b and 0x0009fa3b are acceptable.)
About the Bitwise Operators
The algorithm for myIntBitsToFloat() uses Java's bitwise operators, and you are also required to use them for intToBinaryString() and intToHexString(). You could also, optionally, use them in hexStringToFloat() andbinaryStringToFloat(). Since you might be new to bitwise operators and hexadecimal numbers, this section provides some documentation for them.
Java (like C and many other programming languages) has four
bitwise logical operators that operate on individual bits within
integers. An integer is represented internally using the binary number
system, that is, as a sequence of 0's and 1's. A value of type int
in Java is, for example, a 32-bit integer. The byte, short,
and long data types use, respectively, 8, 16, and 64 bits.
The bitwise logical operators &
, |
,
and ^
are binary operators that operate on a pair of integers,
while ~
is a unary operator that operates on a single integer.
These are the bitwise and, or, exclusive or and not
operators.
When these operators are applied to multi-bit integers, they
apply to each bit position separately.
Their operations on individual bits are defined by this table:
x | y | x & y | x | y | x ^ y | ~x |
---|---|---|---|---|---|
0 | 0 | 0 | 0 | 0 | 1 |
0 | 1 | 0 | 1 | 1 | 1 |
1 | 0 | 0 | 1 | 1 | 0 |
1 | 1 | 1 | 1 | 0 | 0 |
Meaning: | x AND y | x OR y | x XOR y | NOT x |
For example,
00101110 & 10100011 = 00100010
, and
~00101110 = 11010001
.
Java also has three shift operators that work on the level
of bits. The shift operators shift all the bits in a number
a specified number of positions to the left or to the right. The operators
are <<
, >>
, and
>>>
, and they are defined for integers
x
and N
as:
x << N | The left shift operator. Shift the bits in x to the left by N bit positions, losing bits from the left end of x, and filling in vacated positions on the right with zeros |
x >>> N | The logical right shift operator. Shift the bits in x to the right by N bit positions, losing bits from the right end of x, and filling in vacated positions on the left with zeros |
x >> N | The arithmetic right-shift operator. Shift the bits in x to the right by N bit positions, losing bits from the right end of x, and filling in vacated positions on the left with zeros or ones. (Use zeros if x is positive, ones if x is negative.) |
The bit positions in a 32-bit integer are numbered
0, 1, 2, ..., 31, from right to left. Note in particular that
(1 << N)
is an integer that has a 1 in bit position N
and a 0 in every other position. And
(x >>> N)
is a number whose 0-th bit is equal to the N
-th bit of x
.
You can use this to set or test
the N
-th bit in an integer x
:
x = x | (1 << N); | Sets bit N of x to be 1. |
x = x & ~(1 << N); | Sets bit N of x to be 0. |
x = x ^ (1 << N); | Flips the N-th bit of x. |
y = (x >>> N) & 1; | y is the N-th bit of x (0 or 1). |
if ( (x & (1 << N)) != 0 ) | Tests if the N-th bit of x is 1. (Parentheses around 1 << N are required.) |
if ( ((x >>> N) & 1 ) != 0 ) | An alternative way to test if the N-th bit of x is 1. |
You can also work with groups of bits. For example,
the number 15 in binary is 00000000000000000000000000001111.
If you compute y = x & 15
,
then y
has the same bits as x
in
positions 0, 1, 2, and 3, while all the other bits of y
are 0. This is called masking; you have "masked out"
all the bits of x
except for the first 4. By
combining this with a shift, you can get other groups of
four bits out of x
. For example,
(x >> 4) & 15
would get you bits 4, 5, 6, and 7 from x
.
Working with groups of four bits becomes easier if you use hexadecimal numbers. Hexadecimal numbers use the symbols 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, and F. Each symbol represents a group of 4 bits, as shown in this table:
|
|
You can use hexadecimal numbers in Java by prefixing them
with 0x
. For example, 0xF
is 15 (a number
with 1111 in the rightmost four bit positions), and
0xFFFF0000
is the number with 0's in bit positions 0 through 15
and 1's in positions 16 through 31.
Note that this notation is not case-sensitive. The lowercase letters a, b, c, d, e, f are considered to be equivalent to the uppercase A, B, C, D, E, F.
Both hexadecimal and binary numbers can be used in a Java program to represent integers. A hexadecimal literal must start with 0x or 0X — for example, 0xFACE1234. A binary literal must start with 0b or 0B — for example, 0b10010101. A 32-bit integer can be represented by a 32-bit binary literal or by an 8-digit hexadecimal literal.