TEACH DECIMALS A.Sloman July 1988
Decimal Numbers
---------------
TEACH * ARITH should be read before this file.
This file describes so-called 'decimal numbers', that is numbers with
decimal points in them.
CONTENTS - (Use <ENTER> g to access required sections)
-- Decimals and integers
-- Using -dataword- to recognise decimals
-- Single and double decimals
-- Using popdprecision
-- Don't use = or == on decimals
-- An example: computing the mean of a list of numbers
-- More on coercions
-- Exercises
-- Floating point numbers
-- Sensible calculations
-- Reading in decimals
-- Arithmetic operations available
-- More Exercises
-- Further reading
-- Decimals and integers -----------------------------------------------
Numbers without decimal points are integers. The following are
'integers':
3, 1, 0, -126
whereas the following are all decimal numbers:
3.42, 1.07, 0.003, -126.237, 3.2e-3
The last is equivalent to 3.2 times 10 to the power -3, i.e. 0.0032.
3.2e-3 =>
** 0.0032
3.2 * (10 ** -3) =>
** 0.0032
Warning: POP-11 doesn't accept 3.2E-3, or .6 as decimals, as some
languages do. You must have something before the decimal point, even if
it is '0', and use lower-case "e" for the exponent, e.g.: 3.2e-3 or 0.6.
Sometimes decimals are called 'reals'.
-- Using -dataword- to recognise decimals ------------------------------
Try:
dataword(1) =>
** integer
vars x = 5.3 + 2.6, y = 33;
dataword(x) =>
** decimal
dataword(y) =>
** integer
I.e. if you add or multiply or divide two decimal numbers you will get a
decimal number.
-- Single and double decimals ------------------------------------------
POP-11 also has 'double decimal' numbers. These are more accurate
numbers and have the dataword "ddecimal". These take up more space in
the computer, but can be used for more accurate (and slower)
calculations. A decimal number expression typed in directly is
interpreted as a ddecimal, not a decimal. E.g.
dataword(3.4) =>
** ddecimal
Whereas if it is the result of an operation like addition or
multiplication it will normally be a single decimal:
dataword(3.4 + 0.0) =>
** decimal
-- Using popdprecision -------------------------------------------------
You can change the Pop-11 operators so that instead of producing a
single precision decimal result, they produce a double precision result.
true -> popdprecision;
dataword(3.4 * 999) =>
** ddecimal
false -> popdprecision;
dataword(3.4 + 999) =>
** decimal
As the above examples show, operations combining a decimal and an
integer, will produce a decimal result. (This is called "coercion" the
integer is co-erced into the form of a decimal before the operation is
performed.)
-- Don't use = or == on decimals ---------------------------------------
The most important thing to remember about decimal numbers is that they
are inherently imprecise. E.g. 10.0 divided by 3.0 should be an infinite
decimal, whereas the computer does not have enough memory for that, so
it stores an approximation.
10.0 / 3.0 =>
** 3.333333
This is partly because the computer cannot really cope with decimal
numbers and tends to get its sums slightly wrong, so that 1.0 + 1.0
might work out as 1.99999 (or perhaps 2.00001). So if you write:
if num == 678.325 then ...
You might not get the behaviour you had expected. Instead test whether
two numbers are within some "tolerance", i.e. test whether their
difference is less than some specified amount.
if abs(num - 678.325) < 0.00001 then ...
N.B.
ABS (short for absolute 'value') always returns a positive number (more
precisely a non-negative number).
Of course, it is up to you to decide what the "tolerance" should be.
E.g. for some problems you might type:
if abs(num - 678.325) < 0.1 then ...
-- An example: computing the mean of a list of numbers -----------------
The following procedures compute the 'mean', (or average) of a list of
numbers. First a procedure to add up the numbers in a list. Notice that
the number 0.0 is used to start the addition, in order to ensure that
the result is a decimal number.
define sum(list) -> result;
if list == [] then
0.0 -> result
else
hd(list) + sum(tl(list)) -> result
endif
enddefine;
define mean(list);
sum(list) / length(list)
enddefine;
mean([1 2 3 4]) =>
** 2.5
mean([1 2 3]) =>
** 2.0
-- More on coercions ---------------------------------------------------
The arithmetic operations + - and * will return an integer only if both
their arguments are integers.
If given two integers the division operator / returns a RATIO if there
would be a remainder, that is:
8 / 4 =>
** 2 (an integer)
9 / 4 =>
** 9_/4 (a ratio)
If one of the numbers is a decimal then the result will also be a
decimal:
9 / 4.0 =>
** 2.25
9.0 / 4 =>
** 2.25
You can make POP-11 print ratios as if they were decimals by doing:
false -> pop_pr_ratios;
Then:
9 / 4 =>
** 2.25 (a ratio printed as a decimal)
-- Exercises ----------------------------------------------------------
Write a procedure called SQUARE, which takes as argument a list of
numbers, for example:
square([1 2 3 4]) =>
and returns a list of the squares of the number, that is
** [1 4 9 16]
Use this procedure to write MEANSQ, a procedure to compute the average
of the squares of a set of numbers, that is
meansq([1 2 3 4]) =>
** 7.5
Notice, this is not the same as:
mean([1 2 3 4]) * mean([1 2 3 4]) =>
which is:
** 6.25
-- Floating point numbers ----------------------------------------------
Decimal numbers are sometimes called 'floating point numbers', or just
'floats'. This is because their accuracy is measured in terms of 'number
of significant digits' rather than absolute value.
Normally POP11 will not print out a decimal showing all the significant
digits, because it is limited by the value of pop_pr_places, which
defaults to 6.
0.1234567890123456789 =>
** 0.123457
However, you can make it print out more significant digits (if the
number has any) by doing something like:
20 -> pop_pr_places;
You can then see how many significant figures are stored in various
situations:
0.1234567890123456789 =>
** 0.1234567890123457
10 / 3.0 =>
** 3.33333
true -> popdprecision;
10 / 3.0 =>
** 3.333333333333333
In POP11, the first six digits of a decimal number (excluding leading
zeroes) are usually significant no matter where the decimal point is.
However, this will generally depend on the kind of machine that is used.
-- Sensible calculations -----------------------------------------------
Because the decimal point can 'float', multiplying a decimal number by,
say, ten doesn't affect its accuracy.
20 -> pop_pr_places;
false -> popdprecision;
vars x = 10 / 3.0;
x =>
** 3.33333
x * 10 =>
** 33.3333
x * 100 =>
** 333.333
x * 10000000 =>
** 33333300.0
An implication of this is that some operations make sense and others
don't. E.g.:
123456.0 + 654321.0 =>
** 777777.0
is reasonable, as is:
1.23456 + 6.54321 =>
** 7.77777
but the following has a second argument whose precision is spurious:
123456.0 + 6.54321 =>
** 123463.0
The result is accurate to only, six significant figures. (The result
may, as here, be 'rounded up'.)
-- Reading in decimals -------------------------------------------------
In general numbers are read as 'double decimal' numbers (accurate to
more significant figures), but results of computations will be 'single
decimal numbers. e.g. try :
dataword(9/4) =>
** ratio
dataword(2.25) =>
** ddecimal
The accuracy of decimals resulting from compilations can be increased by
assigning TRUE to POPDPRECISION. (See REF * POPDPRECISION.)
-- Arithmetic operations available -------------------------------------
The following procedures are available for manipulating decimal numbers.
SQRT(X)
square root.
sqrt(100) =>
** 10.0
If applied to a negative number, this will produce a "complex" number
with a zero "real" part and a non-zero "imaginary" part.
sqrt(-100) =>
** 0.0_+:10.0
The symbol "_+:" joins two integers or decimal numbers to form a complex
number.
INTOF(X)
Given a decimal number this procedure returns the integer part, for
example:
intof(3.6) =>
** 3
N.B., if X is negative then
intof(x) = -intof(-x)
so
intof(-3.6) =>
** -3
ROUND(X)
Given a decimal number this procedure returns the closest integer, for
example:
round(3.5) =>
** 4
round( -3.5) =>
** -4
REALOF(X)
Given an integer this procedure returns the corresponding decimal
number, for example:
realof(3) =>
** 3.0
If X is an integer then:
x = intof(realof(x))
The above is not a complete list. See below.
-- More Exercises ------------------------------------------------------
Suppose you know that a group of workers earn the following sums of
money a week:
[112.34 96.40 87.05 103.56 99.39]
(rather a small sample!). Extrapolating from this sample how probable do
you consider it that:
the average worker earns, say, 92.07 a week?
Write a procedure which compares this probability.
Suppose, further, that the same group of workers is paid (as opposed to
earns):
[37.24 83.00 130.45 65.75 82.00]
(People don't always get paid what they've earned. E.g. tax may be
deducted!)
Write a procedure to guess what someone is paid given what they earn,
also compute a measure of 'confidence' in this value.
Hint: Think of the problem in terms of fitting a curve to points on a
graph. As a first approximation try fitting a straight line to the
points. The above figures aren't too good for this, try instead a
height/weight table or something similar.
-- Further reading -----------------------------------------------------
TEACH * ARITH
TEACH * STATS
For a summary of available arithmetical facilities, try
HELP * MATH
For fuller information on numbers in POP-11 see HELP * NUMBERS
For complete information see REF * DATA, REF* NUMBERS
--- C.all/teach/decimals -----------------------------------------------
--- Copyright University of Sussex 1987. All rights reserved. ----------Author: A.Sloman July 1988