2.1 Class INTEGER
2.2 Integer declation
2.3 Integer expressions
2.4 Assignment
2.5 Error messages
2.6 Integer I/O
2.7 Output formatting
2.8 Class REAL
2.9 Class DOUBLE
2.10 Mathematical classes
2.11 Class CHARACTER
2.12 Case study
2.13 Main Points
2.14 Exercise

Chapter 2: Basic data types

Keywords: INTEGER, REAL, DOUBLE, CHARACTER, declaration, input, output, assignment

This chapter presents the basic data types in Eiffel:-
      INTEGER    REAL     DOUBLE,     CHARACTER.
The syntax and mechanism for declaration, input, assignment, calculation, and output are given for each type. A data declaration reserves storage for a variable, and gives that variable an initial, default value. An input commands get a value from the keyboard, an assignment statement stores a value in a variable, and an output command displays a value on the terminal.

2.1 Class INTEGER

Integers in Eiffel are instances or objects of type INTEGER. In Eiffel, an INTEGER is stored in 32 bits, so an integer can take any value between 2^31 - 1 and - (2^31 - 1); that is, between + 2, 147, 483, 647 and - 2, 147, 483, 647.

The behaviour of an integer is defined by what you can do with it; formally, the behaviour of a class is defined by the operations in that class. The table below shows the symbol, name, and an example expression for each numeric operator defined in class INTEGER:

+ unary   plus +6

- unary   minus -42

^ exponent   3 ^ 2

* times   hours * rate

/ divide   total / people

// divisor   365 // 30

\\ modulus   hours \\ 12

+ binary plus   3 + total_cost

- binary minus   wins - losses

The unary operators + and - take a single value or argument, and return a single value. The unary minus operator returns the negative of its argument, so the value of --3 is 3. All the other INTEGER numeric operators take two values, and return a single value.

The exponent or power operator takes two numbers, and returns a new number. Let us call the two integer arguments the number and the power, and the returned value the result. The value of the result is found by raising the number to the given power; several examples are given below. In Eiffel, a number raised to a power results in a real number, even if the two arguments are both integers.
 

2 ^ 0 = 1.0 1 ^ 43 = 1.0 0 ^ 28 = 0.0

2 ^ 1 = 2.0 3 ^ 3 = 27.0 -4 ^ 2 = 16.0

2 ^ 2 = 4.0 5 ^ 2 = 25.0 -4 ^ 3 = -64.0

The times operator takes two integers and returns an integer that is the product of its two arguments. The divide operator takes two integers, and returns a single real number that is the value of the first argument divided by the second argument.

The divisor (symbol //, often named div) and modulus (symbol \\, often named mod) operators take two integers as arguments. The divisor returns the number of times that the first integer divides into the second. The modulus returns the remainder after an integer division. Examples of the div and mod operators are
 

12 // 10 = 1 10 goes into 12 1 time (there is 1 10 in 12)

47 // 10 = 4 10 goes into 47 4 times (there are 4 10s in 47)

53 // 25 = 2 25 goes into 53 2 times

12 \\ 10 = 2 12 is 10 * 1, with 2 left over

47 \\ 10 = 7 47 is 10 * 4, with 7 left over

53 \\ 25 = 3 53 is 25 * 2, with 3 left over
 

All INTEGER operators take one (unary operators) or two (binary operators) integer values as arguments. All but the divide and exponent operators return an INTEGER value; divide returns a REAL value, and exponent a DOUBLE precision real value. The integer operators, the types they use, and the types they produce, are shown below; a list of the argument and the returned types is called the signature of the routine.

symbol name use result
 

+ unary plus INTEGER INTEGER

- unary minus INTEGER INTEGER

^ exponent INTEGER, INTEGER DOUBLE

* times INTEGER, INTEGER INTEGER

/ divide INTEGER, INTEGER DOUBLE

// divisor INTEGER, INTEGER INTEGER

\\ modulus INTEGER, INTEGER INTEGER

+ binary plus INTEGER, INTEGER INTEGER

- binary minus INTEGER, INTEGER INTEGER
 


2.2 INTEGER declaration

A declaration reserves storage in the computer's memory to store the value of a variable. Formally, the declaration gives the variable a name, a type, and an initial value, to fill the three parts of a variable (shown below). A variable must be declared before it can be used in code.

A variable declared as a feature of a class is called an attribute. An attribute value is a permanent part of an object, and exists as long as the object exists. If a value is defined in the declaration, the variable is a constant, and the value cannot be later changed; a constant must be declared as an attribute.

Some example attributes and constants are shown below, first their data structure and then their declarations. The first declaration names a single variable of type INTEGER, the second names three INTEGER variables, and the third declaration defines an INTEGER constant.

variable length: REAL

variables length, height, width: REAL

constant length: REAL is 4

The syntax of a declaration is a name, followed by a colon, a space, and the type of the variable. Multiple variables of the same type can be declared in a single declaration by writing several names (separated by commas) before the colon; in this case, a comma then a space is placed between two identifiers. Eiffel follows the English convention for text layout, where a punctuation mark (colon, comma) is placed immediately after the word, then a space is placed between the mark and the next word. This makes Eiffel code easy to read, by adopting the natural language conventions that we all know.

A name (identifier) in Eiffel is a sequence of characters. The first character must be a letter ('a' to 'z', ‘A’ to ‘Z’), but the other characters may be letters, digits ('0' to '9'), or the underscore character ('_'). All identifiers (variable, routine, or class name) obey this rule.

The name of an attribute is a noun that describes that attribute. The name is almost always a single word, but there may be situations where a compound name is needed; the convention is to link simple names with an underscore such as tax_rate, my_name, and top_score.. Two atributes in a class cannot have the same name, because the computer needs a unique name for each variable; this situation is known as a name clash. A variable may have the same name as a class. This is not a name clash, because there are not two variables with the same name: there is a variable and a class with the same name. Eiffel has a set of reserved words, listed in Appendix B, that have a special meaning in the language and cannot be used as names.

The type of a variable is written after the name, separated from the name by a colon. Every type in Eiffel is a class, except for generic classes (see Chapter 11). The simple or basic types of variable in Eiffel are INTEGER, REAL, DOUBLE (double precision real numbers), CHARACTER, and BOOLEAN. Class names are always written in upper case in Eiffel.

Eiffel variables get a default value when they are created. Variables of type INTEGER are given an initial value of 0 (zero). The value is changed by an assignment statement.

A constant is declared by giving the name, type, and value in the attribute declaration. A constant is considered to be part of the class definition, so it has to be declared as an attribute. The name of a constant (such as Void) starts with a capital letter, by convention. Examples of integer constant declarations are
 

Days_in_year: INTEGER is 365

Days_in_week: INTEGER is 7
 

Unique constants represent a set of unique values, and are used to define enumerated data types. A set of values are defined or enumerated in a declaration, the order of these values is defined from left to right in the declaration, and then we can then test and assign such values. The series of names in the declaration are given a series of INTEGER values that are guaranteed to be unique and increasing, but whose exact value is unknown. They represent enumerated types, when the set of possible values can be enumerated and no further detail is needed. The declaration has the form
 
Red, Orange, Yellow, Blue, Green, Indigo, Violet: INTEGER is unique
Each name is a constant, so it can be assigned to an INTEGER variable, and that value can be tested, as in if colour < Yellow then ....

Variable names begin with a lower case letter, constants with an upper case letter, and compound names are connected by an underscore. These are the standard, worldwide Eiffel conventions and should be followed; experienced Eiffel programmers assume that the author has followed the conventions when they read someone else's code.

Great care should be taken to choose the correct name. Programmers spend most of their time reading other people's code, not writing new code. Most of the money spent in computing is spent on changing existing code, not on writing new code. The "software crisis" has arisen because much of the existing code cannot be understood or modified, and has to be either thrown away or re-written from scratch. Eiffel was designed to build reusable software and to help solve the software crisis, so you should always write your code for the next person, and remember that words are very useful in communicating between people.

Common error: two attributes have the same name, generating a name clash

Error code: VMFN

Error: two or more features have the same name

What to do: If they must indeed be different features, choose different names or use renaming.

2.3 INTEGER expressions

Expressions are used to calculate a value; that value may be stored in a variable using an assignment statement, or the value may be used immediately in the code. An expression is a sequence of values connected by operators. The values may be variables, constants, or literals; a literal is an explicit value written as part of the code, such as 0.5 in the expression price * 0.5. The basic way to evaluate an expression is to evaluate the operators in left to right order, but there are two exceptions to this basic rule: operator precedence and explicit bracketing.

Each operator has a specific precedence or priorityor priorityor. When a flat expression (an expression with no brackets) is evaluated, the operators are evaluated first in precedence order, then in left to right order. The precedence order for the INTEGER operators is shown below, from highest to lowest precedence:
 

+ - unary plus, unary minus

^ exponent

* / // \\ times, divide, divisor, modulus

+ - plus, minus

For a flat expression, all exponents are evaluated before any multiplication is done, all multiplication is done before any addition, and so on. Operators at the same level of precedence are executed from left to right in the expression. Brackets are used to force evaluation, because expressions within brackets are evaluated before unbracketed ones.

The rules that the computer uses to evaluate a complex expression are thus:

1. Select an expression in brackets, starting with the deepest brackets

2. In that expression, evaluate all operators at the top level of precedence, left to right

3. In that expression, repeat the evaluation for each level of operator precedence

4. When the flat expression is evaluated, find the next, most nested flat expression

5. Repeat 2-4 until the whole expression has been evaluated

Several example expressions, the order of evaluation, and their results, are shown below. The complete Eiffel operator precedence order is given in Appendix A.
 

1 + 2 * 3 ^ 3 - 4 / 5 (1 + 2 ^ (2 * 3)) / 4

-> 1 + 2 * 27.0 - 4 / 5 -> (1 + 2 ^ 6) / 4

-> 1 + 54 - 4 / 5 -> (1 + 64.0) / 4

-> 1 + 54 - 0.8 -> 65 / 4

-> 55 - 0.8 = 16.25

= 54.2

-73 \\ 12 // 5 + 3 ^ 2 / 4

1 + 2 ^ 2 * 3 / 4 -> -73 \\ 12 // 5 + 9.0 / 4

-> 1 + 4.0 * 3 / 4 -> -1 // 5 + 9 / 4

-> 1 + 12 / 4 -> -1 + 9 / 4

-> 1 + 3.0 -> -1 + 2.25

= 4.0 = 1.25

Brackets should be used to make the expression clear where there is any possibility of confusion. You should always write code with the next person in mind, who has to read and understand your code, and explicit brackets can make an expression clearer to understand.

2.4 Assignment

A variable gets a value when it is created. This value may be changed when the code is executed by an assignment statement. An assignment statement or instruction has the form
 

identifier := expression
A value is produced when the expression on the right-hand side of this statement is executed or evaluated, and the value of the expression is then stored in the variable listed on the left hand side. The Eiffel convention for writing assignment statements is to place a space before and after the assignment operator.

Some examples of integer assignments are shown below:
 

total_cost := air_fare + bus_fare + hotel

area := length * height

count := count + 1

Assignment creates the data flow in a system. Data in a variable on the right hand side of an assignment (the source) "flows" into the variable on the left hand side of the assignment (the target). To illustrate how data values flow through the variables, a code fragment is shown to the left below and the values of the variables at each step are shown to the right.

A data flow diagram shows the data flow, illustrated in the centre below. The values of length and height are used to calculate area, and the values of area and width are used to calculate the volume. A data flow chart shows the data flows in the system, not the order of the operations. We could swap the order of the first two inputs and still have a correct solution.

Assignment is a two step process that occurs in time. The assignment count:= count + 1 is both correct and common; the variable count is incremented by 1, so the value of the variable is larger by one, after the statement has been executed. This is very different from an equality test; it is impossible for a value to be larger than itself. The assignment operator should thus be called something like assigns, gets, or is. The operator should never be called equals; it does not test for equality, it assigns a value to the variable.

2.5 Error messages

If you write code that Eiffel cannot parse, you get an error messsage that describes

1. What the error is

2. Where the error is

3. How to fix the error

This is exactly the information you need to fix your mistake, so it is important to listen to what the Eiffel compiler tells you in the error message. Novices often ignore the content of an error message, however, possibly because it uses words with which they are not familiar.

Consider the simple class shown below:
 

class X

creation
        make

feature
        a, b: INTEGER
        make is
                        -- add two numbers together
                do
                        sum := a + b
                end -- make

end -- class X

This code shows the most common novice error, to forget to declare a variable. You must declare a variable before it can be used; if not, Eiffel gives you the compile time error:

Error code: VEEN

Error: unknown identifier

What to do: <make sure that the identifier is defined>

Class: X

Feature: make

Identifier: sum

The last three lines give the location of the error. In this case, Eiffel could not work out what to do with the identifier sum in the routine make in the class X. By reading the last three lines of the error message, you know the class, the routine, and often the line where the error was found.

The first two lines give the error code (ignore this), and a short description of the error. In this case, the error was that Eiffel ran across an unknown identifier sum in the make routine of class X, so it says "unknown identifier"). An identifier can be the name of a variable, a routine, or a class, so you need to work out which of these is the case, by looking at the name of the identifier on the last line of the error message.

The hardest part of the error message is the "What to do" section, because an error message uses words that have a precise meaning in Eiffel, but that you may not understand. Often the rest of the message provides all the information you need to find and fix the error, as is the case here. The "fix" given in the message above (enclosed in < >) is not even close to the Eiffel error message (run the code to see the message), but the rest of the message is so good you do not need to understand the "What to do" message.

2.6 INTEGER input and output

The word "input" is used here to describe the act of typing data into the computer via the keyboard, and "output" is used to describe data that is shown on the terminal screen. Input and output operations are controlled in Eiffel by a special system object whose name is io, so every input and output command uses the object io, and has the form io.feature. Because Eiffel is a fully OO language, it uses a different command for each type of input and output.

To get a single, integer value from the user requires three lines of code. The first line is a prompt, that writes a string to the terminal screen so the user knows what to do. The second line is a command that reads the value that the user types into the keyboard. The third line takes this value and stores it in a variable in your system. An example of this input code is
 

io.putstring ("Please enter your age in years: ")

io.readint

age := io.lastint

To read an integer, you use a command of the form

io.readint

This command gets a value from the keyboard and stores that value in the Eiffel integer buffer; a buffer is a temporary storage area in the computer. Each input type in Eiffel has its on buffer. After Eiffel executes this instruction, the value from the keyboard has been read and stored, and your Eiffel system can now use that value.

To find out what the entered value was, you ask for the last integer read in using the query

io.lastint
This function returns the last integer value that was read. Because it is a function, it can be called many times and will return the same value each time; by definition, a function changes nothing.

Every input from the user should be preceded by a prompt, so the user knows what to type. You don't need to use quotes to input values; the computer finds out how to treat the input by looking at the type given in your input command. Note that the input command io.readint does not mention a variable. Eiffel stores its input in a buffer, one buffer for each type of object. To access the buffer, you issue a query to get the last value read into the buffer. Getting the last value does not change the value of the buffer, it only reads that value, so you can get the last value as many times as you want. The input will only change when you read in a new value for that type of object. Reading in a new value changes the value stored in the buffer, so any input value must be stored used by your system before the next value is read in from the user.

To output an integer, you write a label to explain the output to the user, followed by an output command. Few things are as puzzling as a number showing on the screen with no explanation of what the value means. A label has the same form as a prompt (it is a string shown on the screen), so the code needed to output an integer is
 

io.putstring ("Your age in years is now ")

io.putint (age)
 

An output is a command, because it changes the state of the screen. The output command, the type it requires, and a sample screen output for the command, are
 
io.putint (x) x: INTEGER 12
Each input command reads a single value, and each output command displays a single value; if you need to read or display several values, then you have to use a separate command each time. Output of a label and three integers thus needs four lines of code. At least one space should be placed between a prompt and its input value, and between a label and its ouput value.

2.7 Output formatting

Prompts and labels are examples of STRING output, where a string is displayed on the screen to communicate with the user. String output uses the command io.putstring and takes a single argument, the string to be displayed. A string is enclosed in double quotes. A long string may be written on two lines by starting every line but the first with a '%' and ending every line but the last with a '%'. Any number of blanks or tabs may be placed between the '%' signs with no effect on the format of the output. An example of a long string output is
 

io.putstring ("Having a wonderful time.%

% Wish you were here.%

% Please send money")

This command produces the screen output

Having a wonderful time. Wish you were here. Please send money.

A new line may be started on the terminal screen by issuing the command io.new_line; this forces the next output to start at the beginning of the next line on the screen. If the next line output is a string, the two lines can be combined by using the new line indicator ‘%N’. The following two pieces of code have the same behaviour:
 

io.new_line

io.putstring ("Gday")

io.putstring ("%NGday")

In the same way, a tab can be written to screen using the special character ‘%T’; a character is enclosed in single quotes, where a string needs double quotes. Special characters are written as two symbols, the special symbol '%' and a following symbol; the new line character is denoted by '%N', the tab character by ‘%T’, and the percent character by ‘%%’. The complete list of special characters is shown in Appendix B. An unprintable character can be specified as an ASCII value using the special character form '%/code/', where the code is the ASCII (decimal) value of the character. A new line character, for example, has an ASCII code of 13, so it can also be specified as '%/13/'.

When an integer is displayed on the screen, Eiffel shows all the digits in the integer, so the number of characters shown on the screen changes with the value of the number. If tabs are used to make the output appear in columns, then each number has to be shown in a fixed length field, so the columns line up on the screen.

The class FORMAT_INTEGER is used to produce fixed length integer output by changing the integer into a fixed length string, and then displaying the string on the screen. An object of this class is created and given a field width as an argument. The feature formatted of this object is then used to print integers with width number of characters. The basic way to format integers for output is shown below, using a field width of 8:
 

class X

creation
        make

feature
        test: INTEGER
        format: FORMAT_INTEGER

        make is
                        -- show how integer formatting is done
                do
                        !!format.make (8) -- create formatting object
                        test := 43
                        io.new_line -- start in column 1 of screen
                        io.putstring ("Value:%T") -- label and tab
                        io.putstring (format.formatted (test)) -- display in width eight
                end -- make

end -- class X

This code looks complex, but is easy to adapt as needed. To display in a different field size, all you need to change is the field width when the object format is created, in line 1 of the make routine here. To display a new variable, all you need to do is to write the name of that variable in brackets after the word formatted; this is the argument to formatted. The feature formatted takes the integer, converts it to a string of size 8 (here) by adding leading blanks if needed, and returns this string value. The string is then output using the normal io.putstring command. If the number of digits is larger than the field width, Eiffel takes as many characters as needed to print all the digits in the value. The class FORMAT_INTEGER has many other formatting features to offer; to see these features, look at the class listing in /opt/Eiffel3/base/support or run short on the class (see Section 5.9).

2.8 Class REAL

A real number is a number with both an integer and a fraction part, and is implemented by class REAL in Eiffel. In Eiffel at SoCS, the integer part of a real number can take on a value between about -10^39 and +10^39, and a fraction value from 0 to about 10^-39.

2.8.1 Declaration and numeric features

Declaration of REAL variables is similar to INTEGER declarations, for both variables and constants. A real number is given the default value 0.0. Examples of REAL declarations are shown below, followed by their data structures;

A REAL constant is written with a decimal point and a fraction; if you simply write an integer value here, Eiffel will report a type mismatch error.

The numeric operations defined on real numbers are similar to those for integers, except that mod and div are only defined for integer values. The REAL operators are shown in their precedence order below:
 

+ unary plus +6.2

- unary minus -42.7

^ exponent 3.6 ^ 2.4

* times hours * rate

/ divide total / people

+ binary plus 3.7 + total_cost

- binary minus total_hours - overtime_hours

Now that we have two numeric types, we need to define how their values are used in an expression. Any numeric value in an expression is first converted into the "heaviest" type in the expression, then the expression is evaluated. A REAL value is heavier than an INTEGER value. If two INTEGERs are added, the result is an INTEGER; if an INTEGER and a REAL number are added then the result is of type REAL; minus, times and divide behave in the same way. Any number of operators can be combined within a single expression.

Because an operator is defined on a type, the effect of the operator can differ depending on the type of its arguments; this technique is called overloading. We have already seen an overloaded operator, the operator make. This operator has the same name in each class, but does different things depending on the code defined in the class. The Eiffel class INTEGER contains the integer operators, and the Eiffel class REAL contains the real operators. Each class contains operators named "+", "-", "*", "/", and "^"; which operator is actually called depends on the type of the arguments. If the arguments are both integers, then Eiffel uses the integer operators. If at least one of the arguments is of type REAL, then the real operators are used. The signatures of the real operators are shown below; note that for the binary operators, if one argument is of type REAL then the other is converted to type REAL before the result is calculated.

symbol name use result
 

+ unary plus REAL REAL

- unary minus REAL REAL

^ exponent REAL, REAL DOUBLE

* times REAL, REAL REAL

/ divide REAL, REAL REAL

+ binary plus REAL REAL

- binary minus REAL REAL

The most common novice error at this point is to use incompatible types; the two sides of an assignment statement, for example, must have compatible types. Two types are compatible if they are the same type, or one can be converted to, and thus stored in, the other. For the numeric types, an INTEGER value can be stored in a REAL variable, but a REAL value cannot be stored in an INTEGER variable.

The code given below produces the following error message:
 

feature
        a, b: REAL
        sum: INTEGER
        ...
        sum := a + b
 
Error code: VJAR

Error: source of assignment does not conform to target

What to do: make sure that type of source (right hand side) conforms to type of target
 

Class: X

Feature: make

Target name: sum

Target type: INTEGER

Source type: <REAL>
 

Eiffel tells you where the error is located: in class X, in routine make, in the identifier sum. The fix part of the message then refers to the source and target of an assignment statement. The source is where the value comes from; the right hand side. The target is where the value goes; the left hand side. The type of the source is REAL, of the target INTEGER, so of course they are incompatible; you can’t store a real number in an integer. All of this information makes your task very simple; you need to simply declare sum to be REAL, and the types of the assignment statement are then compatible. The only tricky part of the message is the word "conform"; informally, "conform" means that two variables either have the same type, or are compatible.
2.8.2 Input and output

Input and output are similar to integer IO, but the operations have slightly different names. To read in a real number, we use

io.readreal to get the value from the keyboard and store it in the REAL buffer, then

io.lastreal to get the value from the buffer and store it in a variable.

To output a real number, we use

io.putreal to display the value on the screen.

An example of REAL input and output is shown below:
 

feature
        test: REAL
        make is
                        -- illustrate REAL input and output
                do
                        io.putstring ("Enter a number: ")
                        io.readreal
                        test := io.lastreal
                        io.putstring ("%NThe value was ")
                        io.putreal (test)
                end -- make
When Eiffel executes the input command io.readreal, it reads a value from the screen and stores it as a real value in the REAL value buffer. You do not have to type in a decimal part if the decimal part is zero, because you have told Eiffel that the input is a real number and it will automatically add a decimal part to the input value if none is explicitly given on the screen. An input value of 3, for example, is stored as the real value 3.0.

When Eiffel executes the output command io.putreal, it displays as many digits as needed to show the value of the number. If the decimal part is zero, then only the integer part is displayed. If the decimal part has only two digits, then only two decimal digits are displayed. Several examples of real values and their screen output via io.putreal are shown below:
 

test := 3.0 io.putreal (test) 3

test := 3.1 io.putreal (test) 3.1

test := 3.12345 io.putreal (test) 3.12345

The command io.putreal expects a real value, and that is the only constraint on io.putreal. The value could be a real literal, a real variable, a real constant, a real expression, or a real function. All that matters to io.putreal is that the value it is given as an argument is a real value; several examples are shown below:
 
io.putreal (3.1) 3.1

test := 3.1

io.putreal (test) 3.1

test := 3.0

io.putreal (test + 0.1) 3.1

io.putreal (sqrt (3.0)) 1.73205

The output can be formatted nicely by converting a number to a string, and then printing the string; the features to do this are supplied by class FORMAT_DOUBLE (there is no class FORMAT_REAL). An object of type FORMAT_DOUBLE is created and used to control the ouput via a format specifier. The format specifier for real numbers gives an output field width and the number of decimal places, so the format specifier has the form (width, precision). The width is the total length of the string, with precision decimal places. A correct output value is always returned, so Eiffel takes as large a field as needed to print the integer part of the value, and uses the specified precision for the decimal part. If the number is too small, leading spaces are added before the output value.

Code to output a field of total length 8, with two decimal places, is shown below. The value displayed on the screen will be eight characters long: three spaces, followed by the two digit integer part, then the decimal point, then two digits for the decimal part.
 

class X

creation
        make

feature
        test: REAL
        format: FORMAT_DOUBLE

        make is
                        -- show how real formatting is done
                do
                        !!format.make (8, 2) -- create formatting object
                        test := 43.789
                        io.new_line -- start in column 1 of screen
                        io.putstring ("Value:%T") -- label and tab
                        io.putstring (format.formatted (test)) -- display in width eight
                end -- make

end -- class X

The class offers many other formatting features.

2.9 Class DOUBLE

A double precision real number is implemented by class DOUBLE in Eiffel. This type of number has the same range of values as a REAL number for the integer part, and can store more precise values in the fraction part of the number. The integer part of a DOUBLE number in Eiffel at SoCS can vary from about -10^39 and +10^39, and the fraction part can have a value from 0 to about 10^-49.

A double variable has a default value of 0.0. Three declarations of type DOUBLE are shown below, with the data structures of the variables. The value is shown with elipses ("...") to indicate the extra precision of the fraction part.

Input is done using the command io.readdouble and the function io.lastdouble. Output is done with the command io.putdouble. Output formatting can be controlled by features of the class FORMAT_DOUBLE. An example of double precision input and output is
 

feature
        test: DOUBLE

        make is
                        -- illustrate DOUBLE input and output
                do
                        io.putstring ("Enter a number: ")
                        io.readdouble
                        test := io.lastdouble
                        io.putstring ("%NThe value was ")
                        io.putdouble (test)
                end -- make

The signature of each DOUBLE operator is shown below:

symbol name use result
 

+ unary plus DOUBLE DOUBLE

- unary minus DOUBLE DOUBLE

^ exponent DOUBLE, DOUBLE DOUBLE

* times DOUBLE, DOUBLE DOUBLE

/ divide DOUBLE, DOUBLE DOUBLE

+ binary plus DOUBLE DOUBLE

- binary minus DOUBLE DOUBLE
 

In summary, the exponent operator always returns a value of type DOUBLE while the other operators may return a value of type INTEGER, REAL, or DOUBLE due to operator overloading.

2.10 Mathematical classes

The class REAL supplies the basic real number opersations to do arithmetic, but there are also many other useful, but complex, operations we can do on real numbers. Sophisticated mathematical functions such as sqrt and sin are provided by the Eiffel class SINGLE_MATH . This class is inherited by the class that wants to use its features; to see the features in the class, you can ask for a short or a flat display by typing
 

short SINGLE_MATH, or

flat SINGLE_MATH
 

The short tool shows you all the features that are defined in a class. The flat tool shows you all the features that are offered by a class. A feature may be immediate (defined in the class) or inherited; inheritance is discussed in Chapter 10.

To find the square root of a real number, for example, we use the class SINGLE_MATH with the code shown below:
 

class X

inherit
        SINGLE_MATH

creation
        make

feature
        test: REAL is 3.0

        make is
                        -- show how to use the inherited function sqrt
                do
                        io.putstring ("The square root of the test value 3.0 is ")
                        io.putreal (sqrt (test))
                end -- make

end -- class X
 

Inheritance is discussed in detail in Chapter 10, but for now all we need to know is to add the keyword inherit and the class to inherit (SINGLE_MATH here) after the class and before the creation clause. When this is done, all features of the class (such as sqrt here) can be used within the class X. The feature sqrt receives a single argument in brackets (the number to use) and returns a real value that is the square root of the argument. This real value is then displayed using the output command io.putreal.

Conversion between numeric types is provided by operators in the REAL and DOUBLE classes (for heavy to light conversions), or automatically by the compiler (for light to heavy conversions). Note that the value is not changed; instead, a function is called on the value and the function returns a new value; the existing value is unaltered. The name and signature of the conversion operators are shown below.

operator name argument result example

truncated_to_real DOUBLE REAL d.truncated_to_real

truncated_to_integer DOUBLE INTEGER d.truncated_to_integer

truncated_to_integer REAL INTEGER r.truncated_to_integer

It is also possible to use these operators with expressions or functions by wrapping brackets around the expression, such as

(3.6 + sqrt (2)).truncated_to_integer => 5

A larger example of type conversion is
 

class X

creation
        make

feature
        test: REAL is 3.12345

        make is
                        -- show how to truncate a real value
                do
                        io.putstring ("The integer part of the test value")
                        io.putreal (test)
                        io.putstring (" is ")
                        io.putint (test.truncated_to_integer)
                end -- make

end -- class X

The numeric classes provide many more features than are used here; you can see all the features in a class by running short or flat on that class.

2.11 Class CHARACTER

A printable character is a character with a value in the set 'a' to 'z', 'A' to 'Z', '0' to '9' as well as punctuation marks ('!', '.', ',', ';', ':', '?'), and logical ('<', '=', '>') and arithmetic ('+', '-', '*', '/') symbols. There are also other, less common printable values such as '@', , '#', '$', '%' and so on. A printable character is written in Eiffel enclosed in single brackets.

A character may also have an unprintable value, such as the new line character that is named CR (Carriage Return), the backspace character (named BS), the end of transmission character (named EOT), and even the character that was used to ring the bell on a teletype (named BEL)! The common unprintable, but useful, characters are shown in Appendix B.2.

The full character set is the standard ASCII (American Standard Code for Information Interchange) character set of 128 characters. A lexical order is defined on the set, based on the ASCII value of each character. For the common printable characters, '0' < '1' < ... < '9' < 'A' < 'B' < ... < 'Z' < 'a' < 'b' < ... < 'z'.

A character is declared like any variable. A variable of type CHARACTER is given an initial, default value of NUL, written as ‘’, and named the null character. Several declarations of type CHARACTER are shown below, with the resulting data structures:

 

A character is input in the normal way, by a procedure (io.readchar) to read the keyboard and store the value in a character buffer, followed by a function (io.lastchar) to return the value of that buffer. As always, every input should be preceded by a prompt to tell the user what to do. Character input thus uses the following basic method:

choice: CHARACTER
                ...
                io.putstring ("Enter your menu choice: ")
                io.readchar
                choice := io.lastchar

Care must be taken with character input, because most of the time your system wants one character but the user actually types in two characters: the printable character and then the new line character CR. Consider the following wrong code:
 

account, choice: CHARACTER

                ...
                io.putstring ("Enter your account id (S, C): ")
                io.readchar
                account := io.lastchar
                io.putstring ("Enter your choice for this account (D, W, S): ")
                io.readchar
                choice := io.lastchar

When the user sees the first prompt, they type in an account identifier, such as 'S' for a savings account and 'C' for a credit account; the quote marks are not actually typed into the keyboard, they are simply used here for clarity. The user then hits the Return key on the keyboard, and the command io.readchar takes the account choice character and stores it in the character buffer, then io.lastchar gets the value from the buffer and returns it so it can be assigned to the variable account. The second io.readchar then reads the next input character, which was a CR!

To overcome this problem, you need to "flush" the unwanted CR from the system. One way to do this would be with a dummy read, but this is an ugly solution; we really don't want to read and then throw away the CR. Instead, we can tell the computer to start reading input from the next input line, after the CR; this is done with the command io.next_line. The correct way to read a single character from a line is thus
 

io.putstring ("Enter your account id (S, C): ")
io.readchar
account := io.lastchar
io.next_line
that throws away the CR. The next input is then read from the start of the next line.

Character output is implemented in the normal way by a command io.putchar. A literal value can be output, such as io.putchar ('D'), but more often a character variable is output. Here is the code to display the user's account choice on the screen:
 

io.putstring ("Your account choice was ")

io.putchar (choice)
 

Declaration, input, assignment, and output have now been covered for the four basic Eiffel types INTEGER, REAL, DOUBLE, and CHARACTER. The remaining basic type, BOOLEAN, is covered in Chapter Five.

2.12 Case study: data flow

The problem specification is

"Money is deposited into and withdrawn from a bank account, and the balance can be displayed. Interest is added daily on the current balance; the interest rate is 4.5% a year."

Main Points in this Chapter

• The numeric types in Eiffel are INTEGER, REAL, and DOUBLE.

• An input command io.read<type> stores an input value in a buffer for each type of object. The last value of that type read from the user is returned from the query io.last<type>.

• An output command of the form io.put<type> shows a data value of that type.

• An assignment statement evaluates the expression on the right-hand side, and stores the value in the variable on the left-hand side

• Operators have a strictly defined precedence order, but this precedence order can be overridden by parentheses

• Operators are defined on types, so an operator can have different effects by overloading.

• The basic type CHARACTER includes both printable and unprintable characters; the carriage return character CR, for example, is a valid character.

Exercises

1. How is an attribute declared? How is a constant declared? What is the difference betwen a literal and a constant? Can an attribute have the same name as a class?

2. What is the default or initial value of an attribute?

3. What are the names of the three numeric types in Eiffel? Is there a class for each type? Is there a text file for each type? How can you get a list of the operators defined for each type?

4. Write down a command to read in:-

• an integer

• a real number

• a double precision real number

• a character

5. An input command gets a value from the user, and stores it in a buffer. How do you get the value from the buffer? How many instructions are there to get a value from an input buffer?

6. Describe the general method for getting a value from the user. Write the declarations and code needed to read and store:-

• two integers

• two real numbers

• an integer and a real number

7. Write down the command to output:-

• an integer

• a real number

• a double precision real number

• a character

8. When do you need a prompt? a label?

9. Explain, step by step, how an assignment statement works.

10. What is meant by operator precedence? What is the numeric operator precedence order?

11. Evaluate the following expressions, showing each step:
 

• 1 + 2 * 3 / 4.0

• 1 // 2

• 34 // 4.5

• 1 // 2 \\ 3

• 1 \\ 2 * 3 // 4 / 5.0

• -43 // 4 ^ 2

• -((12 / 3.0) * (0 // 7) + 2)

12. Write a class X that consists of a single make routine, plus attributes. Write a make routine to read in the weight of an object in pounds, convert the weight to kilograms, and show the answer. The program should print out both the weight in pounds and in kilograms. One pound is equal to 0.453592 kilograms.

13. Write a class X that consists of a single make routine, plus attributes. Write a make routine that reads in an employee's hourly rate, the number of hours worked, and the tax rate. It then finds and shows the gross salary (before tax) and net salary (after tax) for the employee.

14. Write a class X that consists of a single make routine, plus attributes. Write a make routine that converts degrees in fahrenheit to degrees in centigrade. Fahrenheit degrees range from 32 degrees F (freezing point of water) to 212 degrees F (boiling point of water), Centigrade degrees range from 0 (freezing) to 100 (boiling).

15. Write a class X that consists of a single make routine, plus attributes. Write a make routine that finds the time and cost of a car trip. The input data is the distance covered on the trip, the average speed, the number of litres of petrol used per hundred kilometres, and the cost of a litre of petrol.

16. Write a class X that consists of a single make routine, plus attributes. The specification is:

"You have decided to become a rock concert entrepreneur and want to use your knowledge of computing to help with the accounting. Write a system that calculates your individual profit and the total attendance for a rock concert.

There are three ticket prices, the cheap seats at $10, the standard seats at $20, and the special seats for $100 each. The special ticket holders get to sit in the front row, plus a pair of autographed sunglasses, plus a chance at a backstage pass. You must pay for the rent on the stadium, cost of the band, security and insurance. The security is calculated at 32 cents per person attending. The insurance is 3.6% of the income.

You have two partners in this venture, and must split the profits evenly between all three partners. You must pay 12.5% tax on any profits made from the concert. Show the net (after tax) profit per person, and the total attendance at the concert."