Cambridge AS & A Level · Part 3 · Chapter 16
Data Representation
Types the programmer invents, the ways a file can be organised, and how a computer squeezes real numbers into a mantissa and an exponent. Every key term, question, task, worked example and exam question from the chapter is here, with labs for each idea.
Guru Jazzy🎯 Learning objectives
By the end of this chapter you should be able to:
- explain why user-defined types are needed
- Define and use non-composite data types
- Define and use composite data types
- choose and design a suitable user-defined type for a given problem
- explain the methods of file organisation, and pick an organisation and access method for a given problem
- Explain the methods of file access
- Explain hashing algorithms
- describe the format of binary floating-point real numbers
- explain what happens when you change how many bits go to the mantissa and the exponent
- convert binary floating-point numbers into denary, and denary into binary floating point
- Normalise floating-point numbers
- explain why a binary representation is sometimes only an approximation of the real number
- explain how binary representations can cause rounding errors
🧱 16.01 Data types
Chapter 13 (sections 13.01–13.05) showed that every variable has a data type, and that the type must be known before the variable can be used. This chapter adds some more data types.
Built-in data types
For every built-in type, the programming language decides two things:
- the range of values a variable of that type can hold
- the operations that can be used on those values
User-defined data types
Usually a "user" is the person typing input into a running program. But when you write a program, you are a user of the programming language, and that is the user in user-defined data type.
The programmer writes the definition of a user-defined type inside the program. After that, variables of the new type can be declared and used. It is not a built-in type, but you can only define one if the language supports that kind of definition.
Non-composite data types
A non-composite data type is defined without referring to any other data type. Built-in types such as INTEGER and REAL are non-composite, and a user-defined type can be non-composite too.
📅 Enumerated data type
An enumerated type is a user-defined, non-composite type. Its definition lists every possible value:
TYPE
TDirections = (North, East, South, West)
TDays = (Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday) Once the types are defined, you can declare variables and give them values:
DECLARE Direction1 : TDirections DECLARE StartDay : TDays Direction1 ← North StartDay ← Wednesday
Two important points:
- The values look like strings but they are not. They are not written in quotes.
- The values are ordinal: they have an implied order, so you can compare them.
DECLARE Weekend : BOOLEAN DECLARE Day : TDays Weekend = TRUE IF Day > Friday
The enumerated type shows why user-defined types are needed. A language could not have a general built-in enumerated type, because nobody knows the values until the programmer lists them.
🔬 Lab: ordinal values
Pick a value for Day. Each value has a hidden position, starting at 0, and that is what > compares.
🧺 Composite user-defined data types
A composite user-defined type is defined with reference to at least one other type. The two very important examples are:
- Record (from Chapter 13). A language could have a built-in record type, but you are expected to define your own, so its fields exactly match what your program needs. Python does not support records.
- Class: the data type of an object in object-oriented programming (Chapter 27). A language comes with built-in classes, but to use OOP properly the programmer creates classes of their own.
TYPE TStudent
DECLARE Name : STRING
DECLARE DateOfBirth : DATE
DECLARE Form : TForms // a field can use another user-defined type
ENDTYPE Example record type added for illustration; it is not printed in the book.
👉 Pointer data type
A pointer variable holds a reference to a memory location, not an ordinary value. The book walks through five pieces of pseudocode:
- Define a pointer type. You only say what type of data it points to:
TYPE TIntegerPointer ← ^Integer - Declare a variable of the pointer type. No caret (^) is needed here:
DECLARE MyIntegerPointer : TIntegerPointer
- Declare two ordinary integers and give one of them a value:
DECLARE Number1, Number2 : INTEGER Number1 ← 100
- Store the address of another variable in the pointer (
@means "address of"):MyIntegerPointer ← @Number1
- Use the dereferenced value (
^after the pointer means "the value stored at that address"). This puts 200 in Number2:Number2 ← MyIntegerPointer^ * 2
Not every language supports pointers, and those that do use their own symbols instead of ^ and @.
Arithmetic can be done on pointers, so they can be used to build dynamic data structures. Without pointers you may have to declare an array with a large upper bound just in case. With them, the structure can grow while the program runs. The details are beyond this course.
🔬 Lab: step through the pointer example
Press Next to run the book's five steps one line at a time and watch memory change. The addresses are made up.
🔵 Set data type
A set type lets a program create sets and use the operations of mathematical set theory. A set:
- Contains a collection of data values
- has no organisation of the values inside it
- Does not allow duplicates
- supports operations such as: checking whether a value is in the set, adding a new value, removing a value, and adding one set to another
A set variable can only be created if the language supports sets. Sets are hard to classify. Because a set holds many values it is tempting to call it a structured type, but it has no structure, so its members cannot be indexed.
The most useful property is that duplicates are not allowed. To remove duplicates from a list or 1D array, convert it to a set and then back again.
Another example: students are put into a group for each subject they study. If each group is a set, the intersection of the Computer Science set and the Physics set gives the students who study both.
🔬 Lab: set playground
Add names to either set (a duplicate is refused), tap a name to remove it, then try the operations.
Computer Science
Physics
Remove duplicates: list → set → list
🎮 Which data type?
Choose the best user-defined (or built-in) type for each need.
📁 16.02 File organisation
Every day we meet many kinds of file: graphics, word-processing documents, spreadsheets and so on. Whatever the type, the content is stored in a binary code that lets the file be used as intended.
For data that a program reads as input, or writes as output, there are only two file types: a text file or a binary file.
A binary file is organised into records. Each record contains fields, and each field holds a value.
| Text file | Binary file | |
|---|---|---|
| Must be known | Number of items per line and characters per item. If not known, item separator characters are needed. | Number of fields per record. For a string field, its length. Other fields take the number of bytes their internal representation needs. |
| Lines / records | Repeating lines, each ended by an end-of-line character (or characters). | No field separators and no end-of-record character needed. |
My thoughts
There should be. If a program declares a record type with exactly the fields of a file's record, it can read or write a whole file record into one variable in a single operation, and each field automatically gets the right type and size. The two ideas are not the same thing, though: a record type exists only in a running program, while a file record is bytes on a disk, and a text file has no record type behind it at all.
🗂️ Serial, sequential and direct-access files
Serial files
The records are in no defined order. A bank's transaction file is a typical use: every deposit or withdrawal is simply appended, so the only order is the time each record was entered.
Sequential files
The records are ordered by a key field whose values are unique and sequential, though not necessarily consecutive. In the bank example, a sequential file could be the master file of customer accounts. At regular intervals the transaction file is read and every affected account in the master file is updated.
To add a record you could append it and sort the file later. More likely, the file is read in order and each record copied to a new file until the right position is reached. The new record is written there, then the rest of the old file is copied across.
🔬 Lab: add or delete a record in a sequential file
The old file is sorted by key. Choose an action, then press Step to copy one record at a time into the new file.
Old file
New file
My answer
- Why: fields such as names, addresses or comments vary a lot in length. Fixed-length fields must be as long as the longest possible value, which wastes space, and they still cut off anything longer.
- How to read them correctly: store the length before each variable field (or before each record), or end each field with a marker character that cannot appear in the data. The reading program then knows where each field ends. One downside: you can no longer work out where record n starts, so direct access needs an index.
Direct-access files
Sometimes called "random-access" files, but, as with random-access memory, the only random thing is that you can go to any record without reading the file in order. There are two ways to get direct access:
- Keep a sequential file plus a separate index file. Each index record has two fields: the key field value, and the position of that record in the main file.
- Use a hashing algorithm when each record is written to the file.
#️⃣ Hashing
One simple hashing algorithm works when every record has a numeric key field. Divide the key by a suitable number. The remainder is the address in the file for that record. The divisor works best if it is a prime number of about the same size as the expected size of the file.
For simplicity, take 4-digit keys and divide by 1000:
0045 / 1000 gives remainder 45 → address 45 2005 / 1000 gives remainder 5 → address 5 3005 / 1000 gives remainder 5 → address 5
Two facts follow:
- The addresses have no order related to the key values.
- Different keys can give the same address.
If the records have no suitable numeric field, choose a field with letters, look up the ASCII code of each character, add the codes together, and use the remainder of dividing that sum in the same way.
When different keys give the same address it is called a collision (the keys are sometimes called synonyms). The best hashing algorithm spreads the addresses as evenly as possible and keeps collisions to a minimum. Collisions can't be avoided altogether, so there must be a defined way to deal with them. Options include:
- a sequential search for the next vacant address after the calculated one
- keeping some overflow addresses at the end of the file
- a linked list that can be reached from each address
🔬 Lab: hash table
Type a key: digits are divided directly, and text is hashed by adding its ASCII codes. Then press Insert or Find.
- What are the two sets of addresses generated as remainders when the key values 0 to 39 are divided by 10 and by 11?
- State one difference between the two sets of addresses.
- Is there any significant difference between the two sets of addresses?
- 11 is a prime number. Prime numbers are said to give a better spread of use of the addresses in a file. Do you know when this is more likely to be true?
Note: the book mixes two ranges here (1–30 in the set-up, 0–39 in part a). The spread lab below lets you try both.
My answer
a Dividing by 10 gives addresses 0–9, and every address is used exactly 4 times (0, 10, 20, 30 → 0 … 9, 19, 29, 39 → 9). Dividing by 11 gives addresses 0–10: addresses 0–6 are used 4 times (e.g. 0, 11, 22, 33) and addresses 7–10 are used 3 times.
b Dividing by 10 uses 10 addresses; dividing by 11 uses 11 (it adds address 10). Or: with 10 the remainder is simply the last digit of the key.
c Not really. Both spread these consecutive keys almost evenly, with 3 or 4 keys per address, so the number of collisions is about the same.
d When the keys have a pattern, for example they are all even or all multiples of 5. A divisor that shares a factor with that pattern (like 10) sends them to only a few addresses. A prime divisor shares no factor with the pattern, so the keys still spread across all the addresses. Try "multiples of 5" below.
📊 Lab: how evenly does the divisor spread the keys?
🔎 File access
Once the file organisation is chosen and the data entered, you need to decide how the data will be accessed.
- Serial file: normally the whole file is read record by record. To find a particular value, the only option is to read from the start until the target record is found.
- Sequential file: searching may have to be done the same way. But if you know the key field value of the record you want, it is faster, because only the key fields need to be read.
- Direct-access file: the key is put through the hashing algorithm again. It gives the same address as when the record was written, so there is no need to read from the start. Because of collisions, some serial searching may still be needed after the first jump.
Deleting or editing
- Sequential file: the same method as adding: copy records from the old file to a new one until the record to delete or edit is reached, deal with it, then copy all the rest.
- Direct-access file: no new file is needed. A record can be found and edited directly without disturbing anything else. To delete a record, a flag is set in it so later reads skip over it.
Choice of file organisation
| Organisation | Well suited to | Book example |
|---|---|---|
| Serial | Batch processing, or backing up data on magnetic tape | A transaction file |
| Sequential | Applications where one search of the file returns several records | A family-history file: find every record with one family name |
| Direct access | Fast access to one record in a large file | The password file checked whenever one of many users logs in |
A key field in a file is not the same as a primary key in a database table. Primary key values must all be unique; key field values in a file do not have to be. Unique key values can be sensible for some applications, but they are not mandatory.
🎮 Which file organisation?
🔢 16.03 Real numbers
A real number has a fractional part. In denary we can write it simply, or in exponential (scientific) notation. For example, 25.3 can be written as
.253 × 102 or 2.53 × 101 or 25.3 × 100 or 253 × 10−1
For 25.3 the simple form is best, but for very large or very small numbers exponential notation is the only sensible choice.
Floating-point and fixed-point representations
A computer must store a real number as a binary code. One option is fixed point: a set number of bits for the whole-number part and the rest for the fractional part. The alternative is floating point, which has the general form
± M × RE
Some bits hold the significand or mantissa, ±M. The rest hold the exponent or exrad, E. The radix R is not stored: it is always 2.
📏 Eight bits, two ways
To compare the two, suppose a real number must fit in eight bits.
Fixed point (sign and magnitude)
Use the most significant bit as a sign bit, the next five bits for the whole-number part, and the last two bits for the fraction. The gap shows where the binary point is implied.
| Description | Binary code | Denary equivalent |
|---|---|---|
| Largest positive value | 011111 11 | 31.75 |
| Smallest positive value | 000000 01 | 0.25 |
| Smallest magnitude negative value | 100000 01 | −0.25 |
| Largest magnitude negative value | 111111 11 | −31.75 |
Table 16.01 Example fixed-point representations (using sign and magnitude)
Floating point (4-bit mantissa, 4-bit exponent)
Now use four bits for the mantissa and four for the exponent, both in two's complement. The exponent is a signed integer. The mantissa is a fixed-point real value, so the question is where to put its binary point. Tables (a) and (b) show two choices; table (c) shows the exponent values.
| (a) Point after 3rd bit | Denary |
|---|---|
| 011 1 | 3.5 |
| 011 0 | 3.0 |
| 010 1 | 2.5 |
| 101 0 | −3.0 |
| 100 1 | −3.5 |
| 100 0 | −4.0 |
| (b) Point after sign bit | Denary |
|---|---|
| 0 111 | 0.875 |
| 0 110 | 0.75 |
| 0 101 | 0.625 |
| 1 010 | −0.75 |
| 1 001 | −0.875 |
| 1 000 | −1.0 |
| (c) Exponent bits | Integer |
|---|---|
| 0111 | 7 |
| 0110 | 6 |
| 0101 | 5 |
| 1010 | −6 |
| 1001 | −7 |
| 1000 | −8 |
Table 16.02 Coding a floating-point real value in eight bits (four for the mantissa and four for the exponent)
Putting the implied binary point straight after the sign bit gives a smaller spacing between the values that can be stored. That is the preferred option. With it, the most important non-zero values are:
| Description | Binary code | Denary equivalent |
|---|---|---|
| Largest positive value | 0 111 0111 | 0.875 × 27 = 112 |
| Smallest positive value | 0 001 1000 | 0.125 × 2−8 = 1/2048 |
| Smallest magnitude negative value | 1 111 1000 | −0.125 × 2−8 = −1/2048 |
| Largest magnitude negative value | 1 000 0111 | −1 × 27 = −128 |
Table 16.03 Example floating-point representations
Comparing Tables 16.01 and 16.03 shows the much greater range of positive and negative values that floating point gives from the same eight bits.
- Using the methods from Chapter 1 (section 1.01), can you confirm that the denary values in Tables 16.02 and 16.03 are correct?
- Can you also confirm that converting a fixed-format real value from positive to negative (or back) still follows the two's complement rules from Chapter 1 (section 1.02)?
My answer
a Give the place values: with the point after the sign bit the four mantissa bits are worth −1, ½, ¼, ⅛. So 0 111 = ½ + ¼ + ⅛ = 0.875, and 1 010 = −1 + ¼ = −0.75. With the point after the third bit they are worth −4, 2, 1, ½, so 101 0 = −4 + 1 = −3. For Table 16.03: 0 111 0111 = 0.875 × 27 = 112, and 1 000 0111 = −1 × 27 = −128. The floating-point lab below shows each of these when you set the bits.
b Yes. Take 0 110 (0.75): invert to get 1 001, then add 1 in the least significant place to get 1 010, which is −0.75 in the table. The binary point makes no difference to the method. Going back, 1 010 → 0 101 → 0 110 = 0.75.
🎛️ Lab: build a floating-point number
Choose how many bits go to each part, then tap the bits. The orange bit is the sign bit, and the implied binary point comes straight after it.
What this split can store
Try 4 + 4 bits and check Table 16.03. Then give more bits to the mantissa: the gaps between values shrink (better precision) but the largest value falls (smaller range).
⚖️ Precision and normalisation
A floating-point format needs two decisions: the total number of bits, and how they are split between mantissa and exponent. In practice the total is offered as an option when the program is written (single or double precision, for example), but the split is fixed by the floating-point processor. If you did have a choice:
- More mantissa bits → better precision
- fewer exponent bits → smaller range of possible values
To get the best precision you normalise the number (nothing to do with normalising a database). Precision depends on how many mantissa bits are used, so it is best when the mantissa holds the largest possible magnitude.
Using the 8-bit format of Table 16.03, here are three ways to store denary 2:
| Denary | Floating-point binary |
|---|---|
| 0.125 × 24 | 0 001 0100 |
| 0.25 × 23 | 0 010 0011 |
| 0.5 × 22 | 0 100 0010 |
| Denary | Floating-point binary |
|---|---|
| −0.25 × 24 | 1 110 0100 |
| −0.5 × 23 | 1 100 0011 |
| −1.0 × 22 | 1 000 0010 |
Table 16.04 (left) Alternative representations of denary 2 · Table 16.05 (right) Alternative representations of denary −4, using four bits each for mantissa and exponent
When the mantissa has its highest magnitude, the two most significant bits are different. That is how you recognise a normalised number: it starts 01 if positive or 10 if negative.
- Positive: shift the mantissa bits left until they start 0 then 1. For each shift left, subtract 1 from the exponent.
- Negative: shift left in the same way until they start 1 then 0. Ignore bits falling off the most significant end.
My answer
Yes. The "smallest positive value" 0 001 1000 and the "smallest magnitude negative value" 1 111 1000 are not normalised: their first two mantissa bits are the same. If every number must be normalised, the smallest positive value is 0 100 1000 = 0.5 × 2−8 = 1/512, and the smallest magnitude negative value is 1 011 1000 = −0.625 × 2−8 = −5/2048.
A typical system stores all numbers normalised, giving maximum precision and one unique pattern per value, and accepts the slightly smaller range near zero. (Real standards such as IEEE 754 add special "denormal" patterns to fill that gap, which is beyond this syllabus.)
🎮 Normalise it
A random 8-bit mantissa and 4-bit exponent. Type the normalised mantissa and exponent.
🔁 Conversion of representations
Chapter 1 (section 1.01) only converted integers. Now we convert real numbers.
Fixed point first
4.75 looks easy: 4 is 100 and .75 is .11, so 4.75 is 100.11. But a positive number must start with 0, so add a sign bit: 0100.11.
For −4.75, use two's complement on the representation of 4.75:
0100.11 converts to 1011.00 in one's complement
then to 1011.01 in two's complement Check with Method 2 of Worked Example 1.01 (Chapter 1): 1011 is −8 + 3, and .01 is .25, so −8 + 3 + .25 = −4.75 ✓
Most fractions can't be stored exactly
Binary fraction places are worth a half, a quarter, an eighth, a sixteenth and so on. Unless a denary fraction is a sum of these, it cannot be converted exactly. Of .1 to .9, only .5 converts exactly. (This was mentioned in Chapter 1, section 1.03, about storing currency values.)
Method for a positive value
- Convert the whole-number part (Chapter 1, section 1.01).
- Add the 0 sign bit.
- Convert the fractional part using a method from Worked Example 16.01.
- Put the whole-number and fractional parts together into the most significant bits of the mantissa.
- Fill the remaining mantissa bits, and the exponent bits, with zeros.
- Move the binary point by changing the exponent until the number is normalised.
For a negative value, treat it as positive for the first five steps. Then convert the mantissa to two's complement before doing step 6.
Worked Example 16.01 · Converting a denary value to a floating-point representation
Example 1: 8.75
- 8 is
1000; adding the sign bit gives01000. - .75 is
.11in binary. - Together:
01000.11, with exponent 0. - Moving the binary point gives
0.100011, with exponent denary 4. - With 10 bits for the mantissa and 4 for the exponent, the final representation is mantissa
0100011000and exponent0100.
Example 2: 8.63
The first step is the same, but now .63 is converted by the "multiply by two and record the whole-number parts" method:
.63 × 2 = 1.26 so 1 is stored, giving the fraction .1 .26 × 2 = .52 so 0 is stored, giving the fraction .10 .52 × 2 = 1.04 so 1 is stored, giving the fraction .101 .04 × 2 = .08 so 0 is stored, giving the fraction .1010
Multiplying .08 by 2 again would give many zeros before the next 1, so the process stops here: .63 has been approximated as .625. Following steps 3–5 of Example 1, the final representation is mantissa 0100010100 and exponent 0100.
My worked answer
7.75 = 111.11
add sign bit = 0111.11
two's complement : invert → 1000.00, add 1 at the last place → 1000.01
(check: −8 + .25 = −7.75 ✓)
move the point 3 places left so it follows the sign bit:
1.00001 × 2^3
mantissa (10 bits) = 1000010000
exponent (4 bits) = 0011
check: 1.000010000 = −1 + 1/32 = −0.96875; −0.96875 × 2^3 = −7.75 ✓ The mantissa starts 10, so it is normalised. Use the converter below with −7.75, 10 and 4 to see every step.
🔬 Lab: denary → floating point, step by step
Uses the book's method: whole part, sign bit, multiply-by-two for the fraction, normalise, then two's complement for negatives. Extra bits are cut off (truncated), so you can see the approximation.
🎮 Practice: encode and decode
Exam format: 8-bit mantissa and 8-bit exponent, both two's complement, normalised. New numbers every time.
⚠️ Problems with using floating-point numbers
Converting a denary real value to binary almost guarantees some approximation, and the mantissa has only so many bits.
Floating-point numbers are used in long mathematical procedures with repeated calculations, such as weather forecasting with a model of the atmosphere, or economic forecasting. Each result is recorded with a slight approximation. These rounding errors can become significant if a calculation is repeated enough times. The only way to stop them becoming a serious problem is more precision, that is, more mantissa bits. That is why languages offer double precision or quadruple precision.
The other problem is range. In the 8-bit format of Table 16.03 the largest value is 112. A calculation can easily produce more than that: an overflow error (Chapter 1, section 1.02). Floating point can also fail the other way. Divide a very small number by a number greater than 1 and the result may be smaller than the smallest value that can be stored: an underflow error. Depending on the situation, a program might carry on by treating the tiny number as zero, but that is risky.
🔬 Lab: rounding errors build up
Add 0.1 again and again, starting at 0. "Exact" is the true answer. The other columns store the running total after every addition with the given number of mantissa bits (exponent bits are not limited here).
| Format | Stored total | Error |
|---|
🔬 Lab: overflow and underflow
The 8-bit format from Table 16.03 (4-bit mantissa, 4-bit exponent, normalised). Keep doubling or halving the value.
📌 Summary
- Examples of non-composite user-defined data types include enumerated and pointer types.
- Record, set and class are examples of composite user-defined data types.
- File organisation allows for serial, sequential or direct access.
- Floating-point representation of a real number allows a wider range of values to be represented.
- A normalised floating-point representation gives the best precision for the value stored.
- Stored floating-point values rarely give an accurate representation of the denary equivalent.
🃏 Key-term flashcards
Tap a card to flip it.
✅ Quick quiz
📝 Exam-style questions
Write your answer first, then open the model answer. These are my mark-scheme-style points, not official mark schemes.
- Example: an enumerated type, e.g.
TYPE TSeasons = (Spring, Summer, Autumn, Winter)(or a pointer type). - The programmer must write the definition in the program before declaring variables of the type; a built-in type is already defined by the language.
- The programmer decides the values it can take (and their order); for a built-in type the language fixes the range of values and the operations.
(i) A composite data type defined by the programmer, made up of a fixed number of named fields (components), which can be of different data types.
(ii) All the related data about one animal is kept together under one identifier, even though it has different types, so the program is easier to write and understand. A whole record can be passed, stored in an array or written to a file in one go.
(iii)
TYPE TAnimal
DECLARE AnimalName : STRING
DECLARE AnimalAge : INTEGER
DECLARE NumberInZoo : INTEGER
DECLARE Location : STRING
ENDTYPE One mark each for: TYPE … ENDTYPE with a name, then each field with a sensible type.
(i) Records, each made up of fields.
(ii)
- A text file stores data as character codes (ASCII/Unicode), so it can be read and written in a text editor.
- A binary file stores data in its internal representation (e.g. an integer as two's complement bytes), so it is not human-readable.
- A text file needs separators / end-of-line characters; a binary file needs none, because the number and size of fields is fixed and known.
(i)
- Serial: records are stored in the order they were entered (appended), with no other order.
- Sequential: records are stored in order of a key field.
- Direct access: a record's position is calculated from its key, by a hashing algorithm or an index…
- …so it can be read or written without reading the records before it.
(ii) A bank's daily transaction file (or a log of sensor readings). Each new transaction is simply appended in time order; the whole file is processed later in a batch, so no record ever needs to be found on its own.
(iii) A file of user passwords checked at login on a system with many users. Each login needs just one record, the file is large, and the response must be fast; hashing the user ID jumps straight to the record.
(i) The exponent.
(ii) Both negative: each mantissa (10010101 and 11001000) starts with 1, and the most significant bit of a two's complement number is the sign bit.
(iii) The first number (mantissa 10010101): its two most significant bits are different (10). The second mantissa starts 11, so it is not normalised.
(iv) Immediately after the sign bit, between the first and second bits of the mantissa. Normalising removes redundant leading bits (repeated 0s or 1s), so every mantissa bit carries a significant digit. The mantissa has the largest possible magnitude, giving the most significant figures and so the best precision.
(v) Greater precision, because there are more significant bits in the mantissa, but a smaller range, because fewer exponent bits mean smaller maximum and minimum exponents.
For interest: the first number is −0.8359375 × 251; the second is −0.4375 × 217 = −57344.
12 = 1100 .43 × 2 = 0.86 → 0 .86 × 2 = 1.72 → 1 .72 × 2 = 1.44 → 1 .44 × 2 = 0.88 → 0 … 12.43 ≈ 1100.0110… with sign bit 0 1100.011… normalise 0.1100011 × 2^4 (only 7 bits fit after the sign bit) mantissa = 01100011 exponent = 00000100
The value actually stored is 0.1100011 × 24 = 1100.011 = 12.375, an approximation of 12.43.
TYPE ThisDate
DECLARE ThisDay : (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
24, 25, 26, 27, 28, 29, 30, 31)
DECLARE ThisMonth : (Jan, Feb, Mar, Apr, May, Jun, Jul, Aug,
Sep, Oct, Nov, Dec)
DECLARE ThisYear : INTEGER
ENDTYPE
DECLARE DateOfBirth : ThisDate (i) Name the non-composite data type used in the ThisDay and ThisMonth declarations. (ii) Name the data type of ThisDate. (iii) The month value of DateOfBirth needs to be assigned to the variable MyMonthOfBirth. Write the required statement.
(i) Enumerated.
(ii) Record (a composite user-defined type).
(iii) MyMonthOfBirth ← DateOfBirth.ThisMonth
(i)
TYPE LocationRainfall
DECLARE LocationName : STRING
DECLARE Height : INTEGER
DECLARE Rainfall : ARRAY[1:12] OF REAL
ENDTYPE (ii) Any two:
- There are only a few records (at most 100), so reading through the whole file to find one is quick enough.
- New locations can simply be appended at the end; there is no need to rewrite the file to keep it in key order.
- The data for all locations is likely to be processed together, so the order of records does not matter.
Cambridge International AS & A Level Computer Science 9608, Paper 31 Q3, June 2015
(a) 3.5 = 11.1 = 0.111 × 2^2
mantissa 0.1110000 → 01110000
exponent 2 → 00000010
(b) start from +3.5: 01110000
invert 10001111
add 1 10010000
exponent unchanged 00000010
check: 1.0010000 = −1 + 0.125 = −0.875; −0.875 × 2^2 = −3.5 ✓ (c) Mantissa 0.111 = ½ + ¼ + ⅛ = 0.875. Exponent 00000100 = 4. Value = 0.875 × 24 = 14. (Or: move the point 4 places right, 01110.000 = 14.)
(d)(i) Normalised. (ii) The first two bits of the mantissa are different (0 then 1).
(e) Mantissa 10000000 (−1), exponent 01111111 (+127), giving −1 × 2127.
Cambridge International AS & A Level Computer Science 9608, Paper 32 Q1, November 2016
Guru Jazzy · Cambridge AS & A Level Computer Science · Chapter 16 Data representation · From the Cambridge coursebook (Langfield & Duddell); model answers are my own