C and Fortran allow you to mix numeric data types in expressions. This should be avoided as much as possible, however, for two reasons:
Most computer hardware cannot perform operations on two different data types. There are usually machine instructions for adding two integers of the same size, and machine instructions for adding two floating point values of the same size, but usually no machine instructions for adding an integer to a floating point value, adding two integers of different sizes (e.g. a 32-bit integer and a 64-bit integer), or adding floating point numbers of different sizes.
Before mixed data type operations occur, one value must be converted to the exact same type as the other. E.g., when adding an integer value to a floating point value, the two's complement value of the integer is converted to IEEE floating point format and stored in a temporary location. Some CPUs have instructions to convert between native data types. Other CPUs will require several instructions that manually manipulate the bits to perform a conversion. In either case, conversions cause a significant slow-down, as the compiler must insert additional machine instructions between the calculations to convert the values. Adding an integer to a double may take several times as long as adding two integers or adding two doubles.
Promotions occur when two different data types are used as operands to a mathematical operator. In all cases, the value of the lower ranking type is converted to the higher ranking type.
The rules of promotion are as follows:
char
will be promoted when mixed with
any other integer, a short
will be promoted when
mixed with an int
, and an int
will be promoted when mixed with a long
.
A float
is promoted when mixed with a double.
Integer types smaller than int
(char
and short
) are promoted
even when not being mixed with higher types, just to
prevent overflow. Promotion occurs when performing
arithmetic operations on the values and when passing them
to functions, such as printf()
.
This means that adding two char
or
short
values takes
longer than adding two int
values.
The program below demonstrates. The maximum value we
can store in an unsigned short
is 65,535.
32,768 + 32,768 is 65536. When we add a + b
,
the result is 65,536, which requires 17 bits to represent.
The values from a
and b
are
promoted to int
before the addition occurs,
so the expression a + b
has type int
and the expected value of 65,536. However,
when this value is assigned to c
, the leftmost
bit is lost and c
ends up getting 0. The
expression a + b / 2
also has type
int
, but dividing by 2 reduces its value to
something that fits in a short
, so d
gets 32,768, not 0.
#include <stdio.h> #include <sysexits.h> int main(int argc,char *argv[]) { unsigned short a, b, c, d; a = 32768; b = 32768; c = a + b; // a + b is 65,536, beyond the range of c d = (a + b) / 2; // a + b / 2 is 32,768, within the range of d // Output is 32768 32768 0 32768 printf("%d %d %d %d\n", a, b, c, d); return EX_OK; }
Demotions occur only when assigning an expression to a variable of a lower ranking type:
integer :: area real(8) :: height, width height = 4.5 width = 1.3 ! Oops, lost the fractional part! area gets 5, rather than 5.83 area = height * width
Note that when assigning any type of real value to an integer variable, the value is truncated, not rounded.
The type of a variable or constant is never changed. The promoted value is stored in a temporary memory location, and discarded when the program finishes evaluating the expression.
The examples below show all the steps necessary to evaluate a mixed expression, including calculations and implicit conversions. Note how implicit conversions can account for a significant fraction of the total operations.
Note also that some conversions, such as integer to floating point, can be rather expensive.
int a, b, d; double x; a = 4; b = 6; x = 3.0; d = b / a + x * 5.0 - 2.5;
Expression Steps completed Notes d = b / a + x * 5.0 - 2.5 d = 1 + x * 5.0 - 2.5 Integer division b / a 1, not 1.5! d = 1 + 15.0 - 2.5 Double multiplication d = (double)1 + 15.0 - 2.5 Promote 1 to double No work completed d = 16.0 - 2.5 Double addition d = 13.5 Double subtraction d = (int)13.5 Demote 13.5d0 to integer No work completed d = 13 Assign result to d
integer :: a, b, d real(8) :: x a = 4 b = 6 x = 3.0d0 d = b / a + x * 5.0 - 2.5
Expression Steps completed Notes d = b / a + x * 5.0 - 2.5 d = 1 + x * 5.0 - 2.5 Integer division 1, not 1.5! d = 1 + x * dble(5.0) - 2.5 Promote 5.0 to real(8) No work completed d = 1 + 15.0d0 - 2.5 Double multiplication d = dble(1) + 15.0d0 - 2.5 Promote 1 to real(8) No work completed d = 16.0d0 - 2.5 Double addition d = 16.0d0 - dble(2.5) Promote 2.5 to real(8) No work completed d = 13.5d0 Double subtraction d = int(13.5d0) Demote 13.5d0 to integer No work completed d = 13 Assign result to d
Even assuming that the CPU running this program can convert from one data type to another with a single instruction, this code would run significantly faster if all the variables and constants were the same data type to begin with. If the CPU cannot convert data types with a single instruction, this statement will be many times slower than necessary.
The compiler's optimizer may be able to eliminate some promotions at run-time by performing them at compile-time. Nevertheless, mixing data types will usually slow down your code to some extent.
In C, we can convert from one data type to another by simply prefixing the expression with the desired data type in parenthesis. This is known as casting the expression.
int a = 1, b = 2; double x; // Convert value in a to double before division. This will cause an // implicit promotion of the value of b when division occurs and // produce a result of 0.5 instead of 0. x = (double)a / b; // We can explicitly convert both a and b, but the result is exactly the same x = (double)a / (double)b
Explicit data conversions can be performed in Fortran 90 using intrinsic functions. For example, to force a real division of two integer variables, we could do the following:
integer :: a = 1, b = 2 real(8) :: x // Convert value in a to double before division. This will cause an // implicit promotion of the value of b when division occurs and // produce a result of 0.5 instead of 0. x = dble(a) / b
Table 16.10. Explicit Conversion Functions
Function | Description | Returns |
---|---|---|
int(A) | Truncated integer | integer |
int2(A) | Truncated 16-bit integer | integer(2) |
int8(A) | Truncated 64-bit integer | integer(8) |
nint(A) | Nearest integer | integer |
real(A) | Nearest real | real |
dble(A) | Nearest real(8) | real(8) [ double precision ] |
cmplx(R [,I]) | Nearest complex | complex |
dcmplx(R [,I]) | Nearest double complex | double complex [ complex(8) ] |
What are two reasons to avoid mixing data types and mathematical expressions?
When do promotions occur in C and in Fortran?
When do demotions occur?
How do we explicitly convert a value to a different data type in C? In Fortran?
What is the output of the following code segment? ( First try to determine it by reading the code, then type it in and run it to verify. )
int a, b, c; double x, y; a = 1; b = 2; c = 3; x = 4.0; y = 5.0; printf("%d\n", a / b); printf("%d\n", 3 / 4); printf("%d\n", c / 2); printf("%f\n", x / y); printf("%f\n", c / x); printf("%f\n", sqrt(x));
integer :: a, b, c real(8) :: x, y a = 1 b = 2 c = 3 x = 4.0d0 y = 5.0d0 print *, a / b print *, 3 / 4 print *, c / 2 print *, x / y print *, c / x print *, x ** (1/2)