Department of Electrical and Computer Engineering ECSE-221A Introduction to Computer Engineering I Assignment 1 – Data Representation Assigned: Due:
Monday, September 8th, 2003 Monday, September 22, 2003 Assignments box, 6th floor.
Question 1 Computers with 64-bit register sizes are becoming increasingly more visible (e.g. Motorola P5, AMD Opteron, Intel Itanium). Although some “C” compilers support a 64-bit integer type (the aptly named longlong), few implementations (libraries) have a provision for inputting numbers in the range [0, 264-1]. Write a “C” language function that converts a hexadecimal string in the range [0,ffffffffffffffff] into a 64-bit unsigned integer according to the following prototype: int str2udoublelong (char *string, long *doublelong);
Since not all compilers support the longlong datatype, the 64-bit integer will be represented as an array of 2 long integers where doublelong[1] corresponds to the most significant word, and doublelong[0] the least significant word. The function should return 0 if the conversion was successful and –1 otherwise (e.g. the input does not correspond to a valid hex string). You may use any functions in the standard “C” library. Your function should be able to handle variable length strings, e.g., 0xff, 0x123456789abcdef, etc. Validate your function be writing an appropriate test program. Exhaustive testing is not feasible, so you must determine an appropriate set of test cases that can provide reasonable assurance that the function will work correctly over the entire range of inputs. Question 2 Write a “C” language program that inputs a hexadecimal string corresponding to either a single or double precision IEEE floating point number and outputs the corresponding decimal floating point representation. The program has exactly 2 arguments: the first specifying either single or double precision and the second the string that is input to the program. Examples: C\ toieee –s 0x7effffff Single precision interpretation of 0x7effffff is 1.7014117e+038 C\ toieee –s 0xffff Single precision interpretation of
0xffff is 9.1834095e-041
C\ toieee –d 0x7fefffffffffffff Double precision interpretation of 0x7effffffffffffff is 1.797693134862316e+308
Your program should generate appropriate error messages if the input string does not correspond to a valid hexadecimal number of if the options presented to the program are incorrect. It is also important that your results be displayed to appropriate precision given the respective mantissa sizes. As with Question 1, you are free to use any functions in the standard “C” library. To validate this program, compute the following by hand and compare against the program output: 1. 2. 3. 4. 5. 6.
The smallest single precision floating point number greater than zero. The largest single precision floating point number. The single precision floating point number corresponding to 0xc7843712 The smallest double precision floating point number greater than zero. The largest double precision floating point number. The single precision floating point number corresponding to 0xa301425103.
Explain any discrepancies between your hand calculations (i.e. what your calculator produces) against your program output. fpf/September 7, 2003
q1test.c
10/5/03 11:42:04 AM
/*----------------------------------------------------------------------*/ /* Program Name: tq1 */ /* */ /* Description: A program to test the str2doublelong function. */ /* */ /* Usage: % tq1 */ /* */ /* Output: Prints any test errors that occur and */ /* terminates with a SUCCESS message if all tests */ /* passed. */ /* */ /* Author: F.P. Ferrie */ /* */ /* Date: September 7, 2003. */ /*----------------------------------------------------------------------*/ #include <stdio.h> #define NVECTS 30 #define SUCCESS 0 int str2udoublelong(char *string, long *doublelong); /*----------------------------------------------------------------------*/ /* Testing Strategy: */ /* */ /* Assumptions: stdlib functions work as advertised; this implies */ /* that all errors reside in the process of string */ /* manipulation. Hence a reasonable testing strategy */ /* is to generate strings of varying lengths, */ /* including those out of bounds. */ /*----------------------------------------------------------------------*/ char *testvectors[NVECTS] = {"This is not acceptable", "0x Neither is this string", "0x01234fooABCDEF01", "0x0123456789ABCDEF0", "0123456789ABCDEF", "0x0123456789ABCDEF", "0x0123456789ABCDE", "0x0123456789ABCD", "0x0123456789ABC", "0x0123456789AB", "0x0123456789A", "0x0123456789", "0x012345678", "0x01234567", "0x0123456", "0x012345", "0x01234", "0x0123", "0x012", "0x01", "0x0", "0x", "0x10000000", "0xffffffff", "0x100000000", "0xfffffffff",
Page 1
q1test.c
10/5/03 11:42:05 AM "0x1000000000000001", "0xffffffffffffffff", "0x10000000000000001", "0xfffffffffffffffff"};
void main () { int error, i; long doublelong[2]; /*----------------------------------------------------------------------*/ /* Perform the list of trials (above). Print input vs output. */ /* Data are stored in the string array testvectors. */ /*----------------------------------------------------------------------*/ printf("\nBeginning test of str2double with %d trials\n", NVECTS); for (i=0; i #include <string.h> #define SUCCESS 0 #define INVALIDINPUT -1 #define EOL 0 /*----------------------------------------------------------------------*/ /* The easiest way to write this function is to do the conversion in */ /* two passes using the sscanf function to convert the input string. */ /*----------------------------------------------------------------------*/ int str2udoublelong(char *string, long *doublelong) { int strlength, numdigits, lastlower; int upindex, lowindex, rtnval, i; char upper[9], lower[9]; /*----------------------------------------------------------------------*/ /* Check that the leading characters are 0x */ /*----------------------------------------------------------------------*/ strlength = strlen(string); if (strlength < 3 || strlength > 18) return(INVALIDINPUT); else if (string[0] != 0 ' ' || string[1] != x ' ) ' return(INVALIDINPUT);
Page 1
q1.c
10/5/03 11:41:28 AM
Page 2
/*----------------------------------------------------------------------*/ /* Copy the string into two buffers corresponding to the lower and */ /* upper hex digits respectively. Some index calculations need to */ /* be done first. */ /*----------------------------------------------------------------------*/ if (strlength lastlower = lowindex = upindex = }
> 10) { strlength-8; 7; strlength-11;
else { lastlower = 1; lowindex = strlength-3; upindex = 0; }
/* 2 passes
*/
/* 1 pass
*/
/*----------------------------------------------------------------------*/ /* Make sure that end of line terminators are placed at the ends of */ /* each buffer, */ /*----------------------------------------------------------------------*/ lower[lowindex+1] = 0; upper[upindex+1] = 0; /*----------------------------------------------------------------------*/ /* Copy loop */ /*----------------------------------------------------------------------*/ for (i=strlength-1; i>1; i--) {
}
if (i >= lastlower) lower[lowindex--]=string[i]; else upper[upindex--] =string[i];
/*----------------------------------------------------------------------*/ /* At this point least significant digits are in lower and most */ /* significant in upper. Use sscanf to convert hex to binary. */ /*----------------------------------------------------------------------*/ rtnval = sscanf(lower,"%lx",&doublelong[0]); if (rtnval == EOF) return(INVALIDINPUT); doublelong[1]=0; if (upindex != 0) { rtnval = sscanf(upper,"%lx",&doublelong[1]); if (rtnval == EOF) return(INVALIDINPUT); } }
return(SUCCESS);
q1test.out
10/3/03 1:45:58 PM
C:\users\ferrie\221\assign1>testq1 Beginning test of str2double with 30 trials Trial #1 input This is not acceptable is invalid Trial #2 input 0x Neither is this string is invalid Trial #3 input 0x01234fooABCDEF01 returns 0x0001234fabcdef01 Trial #4 input 0x0123456789ABCDEF0 is invalid Trial #5 input 0123456789ABCDEF is invalid Trial #6 input 0x0123456789ABCDEF returns 0x0123456789abcdef Trial #7 input 0x0123456789ABCDE returns 0x00123456789abcde Trial #8 input 0x0123456789ABCD returns 0x000123456789abcd Trial #9 input 0x0123456789ABC returns 0x0000123456789abc Trial #10 input 0x0123456789AB returns 0x00000123456789ab Trial #11 input 0x0123456789A returns 0x000000123456789a Trial #12 input 0x0123456789 returns 0x0000000123456789 Trial #13 input 0x012345678 returns 0x0000000012345678 Trial #14 input 0x01234567 returns 0x0000000001234567 Trial #15 input 0x0123456 returns 0x0000000000123456 Trial #16 input 0x012345 returns 0x0000000000012345 Trial #17 input 0x01234 returns 0x0000000000001234 Trial #18 input 0x0123 returns 0x0000000000000123 Trial #19 input 0x012 returns 0x0000000000000012 Trial #20 input 0x01 returns 0x0000000000000001 Trial #21 input 0x0 returns 0x0000000000000000 Trial #22 input 0x is invalid Trial #23 input 0x10000000 returns 0x0000000010000000 Trial #24 input 0xffffffff returns 0x00000000ffffffff Trial #25 input 0x100000000 returns 0x0000000100000000 Trial #26 input 0xfffffffff returns 0x0000000fffffffff Trial #27 input 0x1000000000000001 returns 0x1000000000000001 Trial #28 input 0xffffffffffffffff returns 0xffffffffffffffff Trial #29 input 0x10000000000000001 is invalid Trial #30 input 0xfffffffffffffffff is invalid End of test... Comments: Trial #3 is clearly wrong! The reason is that the lcc version of the SSCANF function does not do any error checking as to the validity of the input. This VIOLATES our assumption that it does. For the purposes of this assignment, it is not necessary to deal with the limitations of sscanf. (A simple fix would be to check each character individually). Otherwise the function performs as expected...
Page 1
q2.c
10/4/03 11:48:36 PM
Page 1
/*----------------------------------------------------------------------*/ /* Program Name: toieee */ /* */ /* Description: Reads in a hexadecimal string corresponding */ /* to an IEEE floating point number and prints */ /* the corresponding decimal floating point */ /* number. */ /* Usage: */ /* toieee [precision] [string] */ /* */ /* where precision can either be -s (single) */ /* or -d (double) and string a hexadecimal number */ /* of the form 0x... */ /* */ /* Notes: This program is implemented in straightforward */ /* fashion using the standard printf function. */ /* */ /* Author: F.P. Ferrie */ /* */ /* Date: September 5, 2003. */ /*----------------------------------------------------------------------*/ /*----------------------------------------------------------------------*/ /* Includes */ /*----------------------------------------------------------------------*/ #include <stdio.h> #include <string.h> /*----------------------------------------------------------------------*/ /* Prototypes */ /*----------------------------------------------------------------------*/ int str2udoublelong(char *string, long *doublelong); /*----------------------------------------------------------------------*/ /* Main - fetch arguments from command line and do rudimentary check. */ /* Use argument to determine appropriate interpretation of */ /* input string. */ /*----------------------------------------------------------------------*/ int main(int argc, char *argv[]) { int fcnret; unsigned long doublelong[2]; /*----------------------------------------------------------------------*/ /* The key trick in writing this program is to stuff the bits read */ /* in to the program into the appropriate memory locations that */ /* correspond to the single and double precision floating point */ /* numbers respectively. The union construct allows one to overlay */ /* different data types into the same memory location. We create */ /* two of these, one for single and the other for double. */ /*----------------------------------------------------------------------*/ union {
unsigned long ip; float fp; } singlestruct;
/* Single precision
*/
q2.c
10/4/03 11:48:36 PM
Page 2
union {
unsigned long ip[2]; double fp; } doublestruct;
/* Double precision
*/
/*----------------------------------------------------------------------*/ /* First some rudimentary error checking */ /*----------------------------------------------------------------------*/ if (argc != 3) { printf("Usage: toieee [precision] [hex string]\n"); exit(0); } /*----------------------------------------------------------------------*/ /* Then read the input according to the precision argument */ /*----------------------------------------------------------------------*/ if (strcmp(argv[1],"-s") == 0) { sscanf(argv[2],"%lx",&singlestruct.ip); printf("Single precision --> %s is %09.7e\n", argv[2],singlestruct.fp); } /*----------------------------------------------------------------------*/ /* The double precision case is not as easy! There is no sscanf */ /* conversion for a longlong datatype. Here we make use of the */ /* str2udoublelong function from Question 1. */ /*----------------------------------------------------------------------*/ else if (strcmp(argv[1],"-d") == 0) { fcnret = str2udoublelong(argv[2],doublelong); if (fcnret != 0) { printf("Error: %s is not a valid hex string\n", argv[2]); exit(0); } doublestruct.ip[1] = doublelong[1]; doublestruct.ip[0] = doublelong[0]; printf("Double precision --> %s is %17.15e\n", argv[2],doublestruct.fp); } else }
printf("%s is not a valid option for this program.\n", argv[1]);
toieee.out
10/4/03 11:47:16 PM
C : \users\ferrie\221\assign1 > toieee - s 0x00800000 Single precision-- > 0x00800000 is 1.1754944e - 038 C : \users\ferrie\221\assign1 > toieee - s 0x7f7fffff Single precision-- > 0x7f7fffff is 3.4028235e + 038 C : \users\ferrie\221\assign1 > toieee - s 0xc7843712 Single precision-- > 0xc7843712 is - 6.7694141e + 004 C : \users\ferrie\221\assign1 > toieee - d 0x0010000000000000 Double precision-- > 0x0010000000000000 is 2.225073858507201e - 308 C : \users\ferrie\221\assign1 > toieee - d 0x7fefffffffffffff Double precision-- > 0x7fefffffffffffff is 1.797693134862316e + 308 C : \users\ferrie\221\assign1 > toieee - d 0xa301425103 Double precision-- > 0xa301425103 is 3.458957502395122e - 312
Page 1