Read Sams Teach Yourself C in 24 Hours Online
Authors: Tony. Zhang
12 067231861x CH09 4.10.2000 11:02 AM Page 141
HOUR 9
Working with Data
Modifiers and Math
Functions
If at first you don’t succeed, transform your data.
—Murphy’s Laws of Computers
In Hour 4, “Understanding Data Types and Keywords,” you learned about
several data types, such as char, int, float, and double, in the C language.
In this hour, you’ll learn about four data modifiers that enable you to have greater control over the data. The C keywords for the four data modifiers are
• signed
• unsigned
• short
• long
12 067231861x CH09 4.10.2000 11:02 AM Page 142
142
Hour 9
You’re also going to learn about several mathematical functions provided by the C
language, such as
• The sin() function
• The cos() function
• The tan() function
• The pow() function
• The sqrt() function
Enabling or Disabling the Sign Bit
As you know, it’s very easy to express a negative number in decimal. All you need to do is put a minus sign in front of the absolute value of the number. (The absolute value being the distance of the number from zero.) But how does the computer represent a negative number in the binary format?
Normally, one bit can be used to indicate whether the value of a number represented in the binary format is negative. This bit is called the
sign bit
. The following two sections introduce two data modifiers, signed and unsigned, that can be used to enable or disable the sign bit.
It should be noted that some computers may not express negative numbers in the manner described; in fact the C language standard makes no requirement that a sign bit be used, although this is a common method. The important thing to understand is the differences between signed and unsigned data types.
The
signed
Modifier
For integers, the leftmost bit can be used as the sign bit. For instance, if the int data type is 16 bits long and the rightmost bit is counted as bit 0, you can use bit 15 as a sign bit.
When the sign bit is set to 1, the C compiler knows that the value represented by the data variable is negative.
There are several ways to represent a negative value of the float or double data types.
The implementations of the float and double data types are beyond the scope of this book. You can refer to Kernighan and Ritchie’s book
The C Programming Language
for more details on the implementations of negative values of the float or double type.
The C language provides a data modifier, signed, that can be used to indicate to the compiler that the integer data types (char, int, short int, and long int) use the sign 12 067231861x CH09 4.10.2000 11:02 AM Page 143
Working with Data Modifiers and Math Functions
143
bit. (The short and long modifiers are introduced later in this chapter). By default, all the integer data types except the char data type are signed quantities. But the ANSI standard does not require the char data type be signed; it’s up to the compiler vendors. Therefore, if you want to use a signed character variable, and make sure the compiler knows it, you can declare the character variable like this:
signed char ch;
so that the compiler knows that the character variable ch is signed, which means the vari-9
able can hold both negative and positive values. If the char type is 8 bits in length, then a 7
7
signed char would hold values from -128 (that is, 2 ) to 127 (2 -1). By contrast, an unsigned character in this case would range from
8
0 to 255 (2 -1).
The
unsigned
Modifier
The C language also gives you the unsigned modifier, which can be used to tell the C
compiler that the specified data type is only capable of holding non-negative values.
Like the signed modifier, the unsigned modifier is meaningful only to the integer data types (char, int, short int, and long int).
For instance, the declaration
unsigned int x;
tells the C compiler that the integer variable x can only assume positive values from 0
16
to 65535 (that is, 2 -1), if the int data type is 16 bits long; a (signed) int would hold 15
15
values from -32768 (-2 -1) to 32767 (2 ).
In fact, unsigned int is equivalent to unsigned (by itself) according to the ANSI standard. In other words, unsigned int x; is the same as unsigned x;.
Also, the ANSI standard allows you to indicate that a constant is of type unsigned by suffixing u or U to the constant. For instance,
unsigned int x, y;
x = 12345U;
y = 0xABCDu;
Here, the unsigned integer constants 12345U and 0xABCDu are assigned to variables x and y, respectively.
The program in Listing 9.1 is an example of using the signed and unsigned modifiers.
12 067231861x CH09 4.10.2000 11:02 AM Page 144
144
Hour 9
LISTING 9.1
Modifying Data with signed and unsigned
1: /* 09L01.c: Using signed and unsigned modifiers */
2: #include
3:
4: main()
5: {
6: signed char ch;
7: int x;
8: unsigned int y;
9:
10: ch = 0xFF;
11: x = 0xFFFF;
12: y = 0xFFFFu;
13: printf(“The decimal of signed 0xFF is %d.\n”, ch);
14: printf(“The decimal of signed 0xFFFF is %d.\n”, x);
15: printf(“The decimal of unsigned 0xFFFFu is %u.\n”, y);
16: printf(“The hex of decimal 12345 is 0x%X.\n”, 12345);
17: printf(“The hex of decimal -12345 is 0x%X.\n”, -12345);
18: return 0;
19: }
On my machine, the executable file of the program in Listing 9.1 is named 09L01.exe.
(Note that when you compile the program in Listing 9.1, you may see a warning message regarding the assignment statement ch = 0xFF; in line 10 due to the fact that ch is declared as a signed char variable. You can ignore the warning message.) The following is the output displayed on the screen after I run the executable on my computer:
The decimal of signed 0xFF is -1
OUTPUT
The decimal of signed 0xFFFF is -1.
The decimal of unsigned 0xFFFFu is 65535.
The hex of decimal 12345 is 0x3039.
The hex of decimal -12345 is 0xCFC7.
As you see in Listing 9.1, line 6 declares a signed char variable, ch. The int
ANALYSIS
variable x and the unsigned int variable y are declared in lines 7 and 8, respectively. The three variables, ch, x, and y, are initialized in lines 10–12. Note that in line 12, u is suffixed to 0xFFFF to indicate that the constant is an unsigned integer.
The statement in line 13 displays the decimal value of the signed char variable ch. The output on the screen shows that the corresponding decimal value of 0xFF is -1 for the signed char variable ch.
Lines 14 and 15 print out the decimal values of the int variable x (which is signed by default) and the unsigned int variable y, respectively. Note that for the variable y, the unsigned integer format specifier %u is used in the printf() function in line 15.
12 067231861x CH09 4.10.2000 11:02 AM Page 145
Working with Data Modifiers and Math Functions
145
(Actually, you might recall that %u was used to specify the unsigned int data type as the display format in the previous hour.)
Based on the output, you see that 0xFFFF is equal to -1 for the signed int data type, and 65535 for the unsigned int data type. Here, the integer data type is 16 bits long.
Lines 16 and 17 print out 0x3039 and 0xCFC7, which are the hex formats of the decimal values of 12345 and -12345, respectively. According to the method mentioned in the last section, 0xCFC7 is obtained by adding 1 to the complemented value of 0x3039.
9
You may receive different results from this example, depending on the width of the various data types on your system. The important thing to understand is the difference between signed and unsigned data types.
Changing Data Sizes
Sometimes, you want to reduce the memory taken by variables, or you need to increase the storage space of certain data types. Fortunately, the C language gives you the flexibility to modify sizes of data types. The two data modifiers, short and long, are introduced in the following two sections.
The
short
Modifier
A data type can be modified to take less memory by using the short modifier. For instance, you can apply the short modifier to an integer variable that is 32 bits long, which might reduce the memory taken by the variable to as little as 16 bits.
You can use the short modifier like this:
short x;
or
unsigned short y;
By default, a short int data type is a signed number. Therefore, in the short x; statement, x is a signed variable of short integer.
The
long
Modifier
If you need more memory to keep values from a wider range, you can use the long modifier to define a data type with increased storage space.
For instance, given an integer variable x that is 16 bits long, the declaration long int x;
increases the size of x to at least 32 bits.
12 067231861x CH09 4.10.2000 11:02 AM Page 146
146
Hour 9
The ANSI standard allows you to indicate that a constant has type long by suffixing l or L to the constant:
long int x, y;
x = 123456789l;
y = 0xABCD1234L;
Here, the constants of the long int data type, 123456789l and 0xABCD1234L, are assigned to variables x and y, respectively.
Also, you can declare a long integer variable simply like this:
long x;
which is equivalent to
long int x;
Listing 9.2 contains a program that can print out the numbers of bytes provided by the C
compiler used to compile the program for different modified data types.
LISTING 9.2
Modifying Data with short and long
1: /* 09L02.c: Using short and long modifiers */
2: #include
3:
4: main()
5: {
6: printf(“The size of short int is: %d.\n”,
7: sizeof(short int));
8: printf(“The size of long int is: %d.\n”,
9: sizeof(long int));
10: printf(“The size of float is: %d.\n”,
11: sizeof(float));
12: printf(“The size of double is: %d.\n”,
13: sizeof(double));
14: printf(“The size of long double is: %d.\n”,
15: sizeof(long double));
16: return 0;
17: }
I obtain the following output after I run the executable 09L02.exe on my computer: The size of short int is: 2.
OUTPUT
The size of long int is: 4.
The size of float is: 4.
The size of double is: 8.
The size of long double is: 10.
12 067231861x CH09 4.10.2000 11:02 AM Page 147
Working with Data Modifiers and Math Functions
147
In Listing 9.2, the sizeof operator and printf() function are used to measure
ANALYSIS
the sizes of the modified data types and display the results on the screen.
For instance, lines 6 and 7 obtain the size of the short int data type and print out the number of bytes, 2, on the screen. From the output, you know that the short int data type is 2 bytes long on my machine.
Likewise, lines 8 and 9 find the size of the long int data type is 4 bytes long, which is the same length as the float data type obtained in lines 10 and 11.
9
Lines 12 and 13 obtain the size of the double data type, which is 8 bytes on my machine. Then, after being modified by the long modifier, the size of the double data type is increased to 10 bytes (that is, 80 bits), which is printed out by the printf() call in lines 14 and 15.
As with the previous example, your results will likely be different if your system supports data widths that are different from the ones on my machine. By running this program on your own machine, you can determine the widths of these data types for your system.
Adding
h
,
l
, or
L
to
printf
and
fprintf
Format
Specifiers
The printf and fprintf functions need to know the exact data type of the arguments passed to them in order to properly evaluate those arguments and print their values in a meaningful way. The format string for the printf and fprintf functions uses the conversion specifiers d, i, o, u, x, or X to indicate that the corresponding argument is an integer, and is of type int or unsigned int.
You can add h into the integer format specifier (like this: %hd, %hi, or %hu) to specify that the corresponding argument is a short int or unsigned short int.
On the other hand, using %ld or %Ld specifies that the corresponding argument is long int. %lu or %Lu is then used for the long unsigned int data.
The program in Listing 9.3 shows the usage of %hd, %lu, and %ld.
LISTING 9.3
Using %hd, %ld, and %lu
1: /* 09L03.c: Using %hd, %ld, and %lu specifiers */
2: #include
3:
4: main()
5: {
6: short int x;
continues
12 067231861x CH09 4.10.2000 11:02 AM Page 148
148
Hour 9
LISTING 9.3
continued
7: unsigned int y;
8: long int s;
9: unsigned long int t;
10:
11: x = 0xFFFF;
12: y = 0xFFFFU;
13: s = 0xFFFFFFFFl;
14: t = 0xFFFFFFFFL;
15: printf(“The short int of 0xFFFF is %hd.\n”, x);
16: printf(“The unsigned int of 0xFFFF is %u.\n”, y);
17: printf(“The long int of 0xFFFFFFFF is %ld.\n”, s);