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 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. ----------