Lecture Set 6 – Simple Types, Math Functions, and
Expressions
A
lot of this is review, so the reading should go pretty quickly.
Study the examples
shown in the text and in this document to test your understanding of the
reading.
A. Numeric Data Types
There are two types of numeric data that
can be represented in memory
1.
Integer
a) The corresponding C data type is "int"
b) Integers can represent whole numbers that do NOT have a
fractional component such as 1, -10, 35, 450, etc.
c) Integers variables should be used where the numeric value will
not
have a fractional component
such as in counting, etc.
2.
Real
a) The corresponding C data type is called floating point and
can be designated
as either “float” or "double"
b) Floats or doubles can represent numbers that have a fractional
component
such as 1.01, 10.1654,
-35.7, 450.333, etc.
Note that the fractional component
can be zero such as 3.0
c) Floating point (float or double) variables should be used wherever a numeric value can have a fractional component such as in mathematical operations involving division, etc.
B.
Physical representation of numeric data in memory
(See also the document on the Binary Number System)
1. All data in a computer is represented as a series of zeros and ones (binary numbers).
a) Each individual zero or one is called a "bit" and
represents "on" (1) or "off" (0)
b) This is a "binary"
numbering system because each "number" or bit
can represent two (binary)
states: 0 or 1
c) In the
"decimal" numbering system each "number" can represent ten
(decimal) states: 0, 1, 2, 3, 4, 5, 6, 7, 8 or 9
d) 8 "bits" or a series of 8 zeros and ones grouped
together is called a "byte"
e) "Bytes" are used to
represent the various data types in memory
1) char is represented in 1 byte
or 8 bits
2) int is usually represented in 4
bytes or 32 bits
3) double is usually represented
in 8 bytes or 64 bits
The
following table tells a large part of the story concerning the internal
representation of data in the computer.
We will refer back to this table often during the remaining discussion
in this Lecture Set.
C.
Logical representation of numeric data in memory
a) There is little in the C standard about the size of an integer (or of a short int or long int). The standard does require the short ints be no bigger (in bits or bytes) than ints and that ints be no bigger than long ints. Usually, 4 Bytes or 32 bits of memory are used for storing an int, although some C implementions may use as few as 2 bytes (16 bits) or as many as 8 bytes (64 bits).
b) Represented directly as a binary number (32 bits)
Binary Number Representation Integer Binary Arithmetic
----------------------------------------- ---------
------------------------
00000000000000000000000000000000
0
00000000000000000000000100000001
257 2^0 + 2^8
00000000000000000001100000100100
6180 2^2 + 2^5 + 2^11 + 2^12
00000000000001000000000000000000
262144 2^18
c) The sign of a number (negative or positive) is determined by the "sign bit" which is either "on" (1) which means a negative number or "off" (0) which means a positive number
Binary Number Representation Integer Binary
Arithmetic
----------------------------------------- ---------- ------------------------
00000000000000000000000000000000 0
10000000000000000000000100000001 -257
2^0 + 2^8
00000000000000000001100000100100 6180
2^2 + 2^5 + 2^11 + 2^12
10000000000001000000000000000000 -262144
2^18
t
the sign bit
Note that
type int is only an approximate representation of what we know of
(mathematically) as the set of integers.
Why is
this? Is there some sort of limit of
the values of integers that we can store in the computer?
Let’s look at
the Table we saw
earlier.
a.
There is little in the C standard about the size of
float or double precision data other than type float cannot contain more bits
or bytes than type double. Usually,
4 Bytes or 32 bits of memory are used for storing a type float variable, while
32 or 64 bits are used to store type double data.
b. The representation is not as accurate as the representation
of integers and representational error becomes a factor.
c. Despite differences in the number of bits used to represent
them, the floating point representations of type float and type double
data are very much the same. For this reason, the next part of our
discussion focuses on the 32 bit representation of floating point data.
d. Represented in two parts known as the mantissa (“fractional
part” of sorts) and the characteristic (much like an exponent).
·
The mantissa is usually a
binary fraction that is normalized – that is it has a value between
0.5 and 1.0 for
positive numbers
- 0.5 and -1.0 for negative numbers
The mantissa represents the
"fractional" portion of a floating point number.
For example: .1011012
= .67312510
·
The characteristic is an
integer value representing a power that is raised to that the mantissa is
multiplied by 2 to represent the real number.
The characteristic is always positive and is stored in excess
notation (usually, with an 8-bit characteristic, we use excess 128
notation).
·
Thus a number with a base
2 (binary) floating point representation might look like this:
real number = mantissa2 *
(2 ^ exponent2)
·
The sign of a number (negative
or positive) is determined by the "sign bit" which is either
"on" (1) which means a negative number or "off" (0) which
means a positive number
Example
1:
010001012 * .110011 ...2 = 2133-128
x (1/2 + 1/8 + 1/16 + 1/64) 10
= 32 x .67312510
= 21.5410
011111012 * .110011 … 2 =
2125-128 x .79687510 = 2-3 x .79687510
= .0996 …10
e.
Numerical inaccuracies
--
Representational Error: Most real numbers cannot be represented accurately in computer
memory as floating point (type float or double) binary numbers
Example
2: We already saw in class that the simple
real value .10 (one thin dime) connect be accurately represented as a float or
a double precision value due to representational error (see Example 1).
Example
3: The fraction 1/3 is a repeating
fraction which means that the fractional portion repeats infinitely:
1/3 =
0.333333333...repeats infinitely
1/3 is
represented in memory to as many significant digits as can be held in a float
or a double value but the number can never be accurately represented as the
fraction 1/3 because the decimal fraction repeats infinitely. Note that adding 1/3 together 3 times should
be equal to 1 but because of representational error it may not be! Certainly, if you added it to itself a few
thousand times, the representational error would show up, much the same as it
did when we added .10 to itself 10,000 times.
Because
of representational error, you should be very careful when comparing float or
double type data, as the results of calculations using these data are not as
accurate as you might think.
D. Run-time Errors
Run-time
errors cause the program to fail or "crash." Some examples of such run-time errors
related to numeric data are shown next.
1.
Arithmetic overflow – occurs when a number is too large to be represented in
memory
2.
Arithmetic underflow - occurs when a number is too small to be represented in
memory
Programs
often crashes when a numeric value can not be stored in memory. Such errors are considered to be fatal
run-time errors. Division by zero
can cause such as error.
E.
Conversion of Numeric Data
1.
Numerical data types can be converted back and forth
-- int can be
converted to double or float
-- double can be
converted to float and vice versa (although double-to-float may cause some loss
of accuracy)
-- double or float
can be converted to int (but fractional values will be truncated)
-- int can be
converted to char
-- char can be
converted to int
2.
Conversion can be implicit (automatic) or explicit (manual)
·
Implicit (automatic)
conversion
Mixed expressions
§
Expressions that contain
both integer and double values are called mixed expressions
§
In order to evaluate an
expression all of the values MUST be of the same type
The compiler
will ensure that lower level data types are always promoted to higher
level types (int converted to float or double, float to double, etc)
Integer values can be
converted to double values with no loss of information:
Viewed in
external representation, such a conversion is equivalent to adding
".0" to the integer value to create the float or double value. The original integer value is not changed,
but a copy with the .0 added is made.
Integer Value Double Value
------------- ------------
10 10.0
-1025
-1025.0
56 56.0
Converting double or float
values to ints usually results in the
loss of information.
The
fractional part of the float or double value is truncated to create the integer
value. The converted value may be different from the original as a result of
this truncation.
Double Value Integer Value Lost Information? Values Changed?
------------------ ----------------- ---------------------- ---------------------
10.1000 10 0.1000 Yes
56.0005 56
0.0005 Yes
6.0000 6 0.0000 No
Examples:
1) 5 / 2
Both values are integers
so NO conversion takes place,
the expression is
evaluated and the result is an integer
value:
5 / 2 is evaluated which
is equal to 2
Remember that the result is also an integer so
there is
NO fractional portion
that can be represented!
2) 5.0 / 2
The value 5.0 is a double
and the value 2 is an integer
so the expression is a "mixed
expression", the value 2
is converted to the
double value 2.0, the expression is
evaluated and the result
is a double value:
5.0 / 2
2 is converted to 2.0
5.0 / 2.0 is evaluated
which is equal to 2.5
Remember that the result
is also a double so there IS
a fractional portion that
can be represented!
·
Assignment statements
-- The expression
on the right hand side of the '=' sign, is evaluated first and the resulting
value is assigned to the variable on the left hand side of the '=' sign
-- As described
for mixed expressions (above) The resulting value is automatically converted to
the proper type before the assignment takes place
Examples:
int x;
double y;
1) x = 5 / 2;
5 / 2 is evaluated which
is equal to 2
No conversion is
necessary since 2 is an integer value and
the variable x is an
integer variable
2 is assigned to the
variable x
2) x = 5.0 / 2;
5.0 / 2 is evaluated
which is equal to 2.5
2.5 is converted to 2
since the variable x is an integer variable
2 is assigned to the
variable x
3) x = 5.0 / 2.0;
5.0 / 2.0 is evaluated
which is equal to 2.5
2.5 is converted to 2
since the variable x is an integer variable
2 is assigned to the
variable x
4) y = 5.0 / 2.0;
5.0 / 2.0 is evaluated
which is equal to 2.5
No
conversion is necessary since 2.5 is a double value and the variable y is a
double variable
2.5 is assigned to the
variable y
5) y = 5.0 / 2;
5.0 / 2 is evaluated
which is equal to 2.5
No
conversion is necessary since 2.5 is a double value and the variable y is a
double variable 2.5 is assigned to the variable y
6) y = 5 / 2;
5 / 2 is evaluated which
is equal to 2
2 is converted to 2.0 since the variable y is a double
variable
2.0 is assigned to the
variable y
·
Explicit (manual)
conversion
Values
can be explicitly converted from one type to another by using "Type Casting"
operators:
-- int can be
converted to double or float
-- double can be
converted to float and vice versa (although double-to-float may cause some loss
of accuracy)
-- double or float
can be converted to int (but fractional values will be truncated)
-- int can be
converted to char
-- char can be
converted to int
To use
type casting operators we place the new type in parenthesis before the
variable, value, function or expression
-- (int)
- convert the value to an integer value
-- (double) - convert the value to a double
value
-- (char)
- convert the value to a char value
Explicit type casting
supercedes any automatic type casting
Examples:
char w;
int x;
double y;
a) y = (int) 5.0 / (int) 2.0;
5.0 is type cast as an
integer and is converted to 5
2.0 is type cast as an
integer and is converted to 2
5 / 2 is evaluated which is
equal to 2
2 is converted to 2.0 since
the variable y is a double
variable
2.0 is assigned to the
variable y
b) y = (double) 5 / (double) 2;
5 is type cast as a double and
is converted to 5.0
2 is type cast as a double
and is converted to 2.0
5.0 / 2.0 is evaluated which
is equal to 2.5
No conversion is necessary
since 2.5 is a double value and
the variable y is a double
variable
2.5 is assigned to the
variable y
c) x = (int) 'A';
'A' is type cast as an
integer and is converted to the ASCII
code 65
No conversion is necessary
since 65 is an integer value and
the variable x is an integer
variable
65 is assigned to the
variable x
d) w = (char) 65;
65 is type cast as a
character and is converted to the ASCII
character 'A'
No conversion is necessary
since 'A' is a character value and
the variable w is a
character variable
'A' is assigned to the
variable w
F. The Character Data Type
a. Every character we use has a unique numeric value
associated with it called an ASCII
(American Standard Code for Information Interchange) code. The character collating sequence is the
ordering of characters according to the codes number associated with the
character. Thus we see that:
A < B
< C < … < Z a < b
< c < …. < z 0 < 1 < 2 < … < 9
and
+ <
/ < 0 < A < a
Code Character Code Character Code Character Code Character
------ ------------- ------- ------------- -------
------------- ------- -------------
1 SOH 33 !
65 A 97 a
2 STX 34 " 66 B
98 b
3 ETX 35 #
67 C 99
c
4 EOT 36 $
68 D 100 d
5 ENQ 37 %
69 E 101
e
6 ACK 38 & 70 F
102 f
8 BS 40 (
72 H 104 h
9 TAB 41 )
73 I 105 i
10 LF 42 *
74 J 106 j
13 CR 45 -
77 M 109 m
14 SO 46 .
78 N 110
n
15 SI 47 /
79 O 111
o
16 DLE 48 0
80 P 112
p
17 DC1 49 1
81 Q 113
q
18 DC2 50 2
82 R 114
r
19 DC3 51 3
83 S 115
s
20 DC4 52 4
84 T 116
t
21 NAK
53 5 85 U 117
u
22 SYN 54 6
86 V 118
v
23 ETB 55 7
87 W 119
w
24 CAN 56 8
88 X 120
x
25 EM 57 9
89 Y 121
y
26 SUB 58 :
90 Z 122
z
27 ESC 59 ;
91 [ 123
{
28 FS 60 < 92 \ 124
|
29 GS 61 =
93 ] 125
}
30 RS 62 > 94 ^ 126 ~
b. The ASCII codes are ordered and can be compared to each other in the same way that other numeric values can be compared to each other
ASCII Code: 49 < 50 < 51 <
52
Character: 'A' < 'B' < 'C' <
'D'
ASCII Code: 65 < 66 < 67 <
68
Character: 'a' < 'b' < 'c' <
'd'
ASCII Code: 97 < 98 < 99 <
100
c. The ordering of
the ASCII codes from lowest to highest is called a collating sequence
and allows character comparison for things like alphabetic ordering, etc.
3. Logical representation of character data in
memory
a. 1 Byte or 8 bits of memory required
b. Represented directly as a binary number within the range of
the ASCII codes from 0 to 127
-------------------- ---------------- ------------------------ -------------
00000000 0 0 NUL
00100000 32 2^5 SPACE
01000100 68 2^6 + 2^2 $
G. Enumerated Types
a. An enumerated
type allows you to associate a meaningful name with a constant numeric code
called an enumeration constant.
b. References to
the meaningful name in the C code are translated by the
compiler into
references to the associated constant numeric code in
much the same
way that the pre-processor substitutes #define MACROs
for their
corresponding values before the program is compiled.
Example 1:
enum day
{MONDAY , // Automatic, assigned numeric code 0
TUESDAY , // Automatic,
assigned numeric code 1
WEDNESDAY, // Automatic, assigned numeric code 2
THURSDAY , // Automatic, assigned numeric code 3
FRIDAY , // Automatic,
assigned numeric code 4
SATURDAY , // Automatic, assigned numeric code 5
SUNDAY // Automatic, assigned numeric code 6
};
This
definition of the enumerated type day simply enables us to associate the integers 0, 1, … , 6
with the names MONDAY, TUESDAY, … SUNDAY.
c. Enumerated type
declarations contain a list of unique meaningful names
and,
optionally, the associated constant numeric codes.
1) The first
meaningful name in the enumeration list is automatically
assigned the
constant numeric code 0, the next meaningful name is
automatically
assigned the constant numeric code 1, and so on unless
explicit
constant numeric codes are specified.
2)
If not all of the
constant numeric codes are specified the unspecified constant numeric codes
continue the progression from the last constant numeric code specified.
3)
The meaningful names
in the enumeration list MUST be unique but the constant numeric codes do NOT
have to be unique.
Example
2: Starting an enumerated list at a point
other than 0
enum month
{jan = 1, feb, mar, apr, may, jun, jul,
aug, sep,
oct = 10, nov, dec};
The
identifiers (also called enumerators) jan … sep represent the integers 1 .. 9. The identifiers oct, nov, dec represent 10, 11, and 12, respectively. Note the explicit reference to the integer 10 in oct = 10 was not necessary
here, but was done for illustration purposes.
Example
3:
enum hour
{t1am = 1, t2am, t3am, t4am, t5am, t6am,
t7am, t8am, t9am,
t10am, t11am, t12pmnoon,
t1pm = 1, t2pm, t3pm, t4pm, t5pm, t6pm,
t7pm, t8pm, t9pm,
t10pm, t11pm, t12ammid
};
The times
t1am … t12pmnoon
are associated with the integers 1 … 12 respectively. The times t1pm … t12ammid are also associated with these same integers.
Example 4:
enum day
{MONDAY , // Automatic,
assigned numeric code 0
TUESDAY , // Automatic,
assigned numeric code 1
WEDNESDAY , // Automatic, assigned numeric code 2
THURSDAY , // Automatic,
assigned numeric code 3
FRIDAY = 7, // Manual , assigned numeric code 7
SATURDAY , // Automatic,
assigned numeric code 8
SUNDAY // Automatic, assigned numeric code 9
};
Example
5:
All
constant numeric codes specified -- The first meaningful name in the
enumeration list is manually assigned a constant numeric code of 1, the next
meaningful name is manually assigned the constant numeric code 3, etc.
enum day
{MONDAY = 1 , // Manual,
assigned numeric code 1
TUESDAY = 3 , // Manual, assigned numeric code 3
WEDNESDAY = 5 , //Manual, assigned numeric code 5
THURSDAY = 7 , // Manual,
assigned numeric code 7
FRIDAY = 9 , // Manual,
assigned numeric code 9
SATURDAY = 11, // Manual, assigned numeric code 11
SUNDAY = 12 // Manual,
assigned numeric code 12
};
d. General form of
the enum declaration
enum
[tag] { list } [variables];
where
tag - optional enumerated
type template name
list - comma ',' separated list of unique meaningful names
and,
optionally, associated constant numeric codes
variables - optional comma ','
separated list of enumerators
Example 6:
enum classId
{freshman =1 , // Manual, assigned numeric code 1
sophomore , // Automatic, assigned numeric code 2
junior , // Automatic, assigned numeric code 3
senior , // Automatic, assigned numeric code 4
graduate = 6 // Manual,
assigned numeric code 6
};
classId myClass;
switch
(myClass)
{
case freshman:
case sophomore:
case junior:
case senior:
printf(“I am an
undergraduate student.”);
break;
case graduate:
printf(“I am a graduate student.”);
break;
default:
printf(“Error in student code.”);
printf(“\n”);
}
H. Predefined Functions in math.h
1. abs
· Returns the absolute value of a number
· int function expecting a single int argument
Examples:
abs (-10) is equal to 10
abs ( 10) is equal to 10
-abs (-10) is equal to -10
-abs ( 10) is equal to -10
abs (-1.45) is equal to 1
(argument is treated as integer)
2. sqrt
· Returns the square root of a number
· double function expecting a single double argument
Examples:
sqrt (25.0) is equal to 5.0
sqrt (16.0) is equal to 4.0
3. pow
· Returns argument 1 raised to the power of argument 2
· double function expecting two double arguments
Examples:
pow (3.0, 2.0) is equal to 9.0 (3.0 raised to the power of 2.0)
pow (2.0, 3.0) is equal to 8.0 (2.0 raised to the power of
3.0)
pow (x, y) is the value of x raised to the power of y
4. floor
Examples:
floor ( 4.5) is equal to
4.0
floor (-4.5) is equal to -5.0 (Note that -5.0 is less than -4.5)
5. ceil
· Double function expecting a single double argument
Examples:
ceil ( 3.789) is equal to
4
ceil ( 2.123) is equal to
3
ceil (-1.345) is equal to -1
6. sin
Examples:
sin (1.570795) is equal to 1
sin (3.14159) is equal
to 0
7. cos
Examples:
cos (1.570795) is equal to
0
cos (3.14159) is equal
to –1
8. tan
Examples:
tan (0) is equal to 0
tan (1) is equal to 0.01745506
I. Compound Operators
1. The addition, subtraction, multiplication and division compound operators are binary operators that require two operands:
operand1 operator operand2; x += 3.0; x /= 4.4;
If x is 5.8 initially, then its value after the execution of the above two statements would be 2.2.
These operators are simply a SHORTHAND for the computation
operand1 = operand1 operator operand2; x = x + 3.0; x = x / 4.4;
Operator Meaning
--------- --------------------------------------------------------------------------------------
+= Add the current value of operand1 to the value of operand2,
store the result back in operand1
-= Subtract the value of operand2 from the current value of
operand1, store the result back in operand1
*= Multiply the current value of operand1 by the value of
operand2, store the result back in operand1
/= Divide the current value of operand1 by the value of
operand2, store the result back in operand1
2. The increment (increase) by 1 and decrement (decrease) by 1 compound operators are unary operators that require one operand
operator operandi i++ counter--
operand operator; counter++
These operators are also SHORTHAND for computations such as
i = i + 1; counter = counter –1;
Operator Meaning
---------- ---------------------------------------------------------------------
++ Increment (increase) the current value of the operand by 1,
store the result back in the operand
-- Decrement (decrease) the current value of the operand by 1,
store the result back in the operand
The timing of the change in value using the ++ or -- compound operators IN FRONT OF versus IN BACK OF the operand can be significantly different!
· If you use the ++ or -- compound operators IN BACK OF the operand then 1) the current value of the operand will be used where ever it appears FIRST, and then 2) the operand will be updated.
· If you use the ++ or -- compound operators IN FRONT OF the operand then 1) the current value of the operand will be updated FIRST and 2) the updated value used as the value of the operand
Let i be equal to 5. Then
k = 3 + --i; // the value
7 will be stored in k
// the value of i will be 4
k = 3 + i--; // the value
8 will be stored in k
// the value of i will be 4
This information is summarized in the following table.
Expression Meaning
------------- ---------------------------------------------------------------------------
++operand Increment (increase) the current value of the operand by 1
FIRST and then use the updated value of the operand
operand++ Use the current value of the operand FIRST and then
increment (increase) the value of the operand by 1
--operand Decrement (decrease) the current value of the operand by 1
FIRST and then use the updated value of the operand
operand-- Use the current value of the operand FIRST and then
decrement (decrease) the value of the operand by 1
If the operand is not USED as part of an expression then the placement of the ++ or – compound operators is NOT significant because the timing of the change in value is NOT significant.
Note that ++ or -- compound operators appearing with operands that are part
of assignments, expressions, conditions, etc. are considered to be USED and the timing of the change in value may be VERY significant.
The use of ++ or -- compound operators in stand-alone assignments are NOT considered to be USED and the timing of the change in value is NOT significant.
3. Equivalent "long-hand" and "short-hand" assignment statements
· Add 1 to the value of the variable x
a) x = x + 1; // Long-hand assignment statement
b) x += 1; // Binary Compound Operator += assignment
//
statement
c) ++x; // Unary Compound Operator ++
assignment
//
statement
d) x++; // Unary Compound Operator ++
assignment
//
statement
· Add 5 to the value of the variable x
a) x = x + 5; // Long-hand assignment statement
b) x += 5; // Binary Compound Operator += assignment
// statement
· Subtract 1 from the value of the variable x
a) x = x - 1; // Long-hand assignment statement
b) x -= 1; // Binary Compound Operator -= assignment
//
statement
c) --x; // Unary Compound Operator --
assignment
// statement
d) x--; // Unary Compound Operator – assignment
// statement
· Multiply the value of the variable x by 5
a) x = x * 5; // Long-hand assignment statement
b) x *= 5; // Binary Compound
Operator *= assignment
// statement