CSC 151-02, Fall 2006 : Schedule : Reading 5

**Summary:**
While Scheme excels at symbolic and list processing, it is also quite
capable of doing numeric computation. Scheme provides a variety of
procedures for dealing with a variety of categories of numbers. In
this reading, we discuss the various numbers and related numeric
operations.

**Contents:**

Computer scientists write algorithms for a variety of problems. Some types of computation, such as representation of knowledge, use symbols and lists. Others, such as the construction of Web pages, may involve the manipulation of strings (sequences of alphabetic characters). However, a significant amount of computation involves numeric computation, such as you might do with a calculator.

One advantage of doing numeric computation with a programming language,
like Scheme, is that you can write your own algorithms to make the
computer
automate repetitive tasks. As you do numeric computation in any
language,
you must first discover what *types of numbers* the
language supports
(some languages support only integers, some only real numbers, some
combinations) and what *numeric operations* the
language supports.
Fortunately, Scheme supports many types of numbers (as you may have
discovered in the first few labs) and a wide variety of operations on
those
numbers.

Scheme treats numbers slightly differently depending on whether they
are
*integers* (whole numbers), *rational
numbers* (expressible
as a ratio of integers), *real numbers*
(corresponding to points on
a number line), or *complex numbers* (corresponding
to points on the
plane determined by a real-number line and a perpendicular line for
imaginary numbers

, the square roots of negative
numbers). From the
Scheme programmer's point of view, these categories of numbers are
nested:
all integers also qualify as rational numbers (5 is the same thing as
5/1);
all rationals count as real numbers, and all real numbers as complex
numbers. Mathematically speaking, the converse inclusions do not
generally hold. (3/4 is rational but not an integer, the square root of
2
is real but not rational, and the square root of -1 is complex but not
real.)

Scheme supplies a predicate for each of these categories of numbers:
`integer?`

, `rational?`

,
`real?`

, and
`complex?`

.
(You may recall that Scheme responds
`#t`

for yes

and `#f`

for no

.)

>(integer? 5)

#t

>(rational? 5)

#t

>(real? 5)

#t

>(complex? 5)

#t

>(integer? 3/4)

#f

>(rational? 3/4)

#t

>(real? 3/4)

#t

>(complex? 3/4)

#t

>(integer? (sqrt -1))

#f

>(rational? (sqrt -1))

#f

>(real? (sqrt -1))

#f

>(complex? (sqrt -1))

#t

Within each of these categories of numbers, Scheme distinguishes
between
*exact numbers*, which are guaranteed to be
calculated and stored
internally with complete accuracy (no rounding off), and
*approximations*, also called *inexact
numbers*, which are
stored internally in a form that conserves the computer's memory and
permits faster computations, but allows small inaccuracies (and
occasionally ones that are not so small) to creep in. Since there's no
great advantage in obtaining an answer quickly if it may be incorrect,
we
shall avoid using approximations in this course, except when the data
for
our problems are themselves obtained by inexact processes of
measurement.

To determine whether Scheme is representing a particular number exactly
or
inexactly, use one of the predicates `exact?`

and
`inexact?`

.

>(exact? 5/9)

#t

>(exact? 13.2)

#f

>(inexact? 13.2)

#t

>(exact? (sqrt 2))

#f

DrScheme happens to store real numbers in such a way that any real
number
that can be named or computed also counts as rational. For instance,
when DrScheme computes `(sqrt 2)`

, the value it
returns is
an inexact approximation to the correct value, and it turns out that
DrScheme uses only rational numbers, even when the values it is trying
to approximate are irrational.

>(real? (sqrt 2))

#t

>(rational? (sqrt 2))

#t

>(integer? (sqrt 2))

#f

The standard language definition for Scheme says that an implementation of the language does not have to support all these categories of numbers; it would be legal, for instance, to leave out complex numbers or to treat all numeric values as inexact. However, most implementations (including DrScheme) support all the kinds of numbers described here.

The built-in Scheme procedure `exact->inexact`

takes an exact number as its argument and returns an inexact
approximation to it:

>12/7

1 5/7

>(exact->inexact 12/7)

1.7142857142857142

>(exact? (exact->inexact 12/7))

#f

Since DrScheme uses fractional notation to print out exact numbers, but renders approximations as decimals, invoking this procedure is a simple way to determine the general format in which results are printed. As we'll see later in the semester, however, there are better ways that give the programmer finer control over the format.

The Scheme standard does not directly support the familiar category of
*natural numbers*, but we can think of them as being
just the same
things as Scheme's exact non-negative integers.

When you write a numeral into a Scheme program or type one in as part of a definition or command to the interactive interface, the structure of the numeral you type determines the category of the number represented.

One basic rule is that a numeral that contains a decimal point normally
stands for an approximation rather than an exact number. Scheme assumes
that you may have rounded off the last decimal place and takes this as
implicit permission to use a rounded-off representation. If you want
Scheme to interpret the numeral as an exact number, you can either
convert
it to a fraction -- for instance, changing `1.732`

to
`1732/1000`

-- or attach the *exactness
prefix*
`#e`

at the beginning of the numeral, making it
`#e1.732`

.

Conversely, a number written such as a sequence of digits (possibly
with a
sign at the beginning) or as a fraction normally stands for an exact
number. If you want an approximation instead, use an equivalent numeral
with a decimal point or attach the *inexactness prefix*
`#i`

. (So `23/70`

is an
exact number, but
`#i23/70`

is an approximation.)

Scheme permits the use of a version of *scientific notation*,
in
which a real number is expressed as the product of some coefficient and
some integer power of 10. For instance, the numeral `3.17e8`

denotes the real number three hundred and seventeen million -- that is,
3.17 times ten to the eighth power. The part of the numeral that
precedes
the `e`

is the coefficient; the part that
follows indicates the
power of ten by which the coefficient should be multiplied. A number
expressed in scientific notation is also inexact unless the numeral is
preceded by the
exactness prefix. DrScheme uses scientific notation when printing out
an approximation if its absolute value is either very large or very
small,
but it never uses scientific notation when printing out an exact
number.

Section
6.2.5 of the *Revised ^{5}
report on the algorithmic language Scheme* lists
Scheme's primitive
procedures for numbers. Read through the list at this point to get a
feel for what Scheme supports. The following notes explain some of the
subtler features of commonly used numerical procedures.

The addition and multiplication procedures, `+`

and `*`

, accept any number of
arguments. You can, for instance, ask Scheme to imitate a cash register
with a command like this one:

>(+ 1.19

.43

.43

2.59

.89

1.39

5.19

.34

)

12.45

You can call the `-`

procedure or the `/`

procedure to operate on a single argument. The `-`

procedure returns the additive inverse of a single argument (its
negative), the result of subtracting it from 0.

There are four procedures relating to division (`/`

,
`quotient`

, `remainder`

,
and `modulo`

).
The `/`

procedure returns the multiplicative
inverse of a
single argument (its reciprocal), the result of dividing 1 by it.
The `quotient`

and `remainder`

procedures apply only
to integers and perform the kind of division you learned in elementary
school, in which the quotient and the remainder are separated: Four
goes into thirteen three times with a remainder of one

:

>(quotient 13 4)

3

>(remainder 13 4)

1

>(quotient 1 2.5)

quotient: expects type <integer> as 2nd argument, given: 2.5; other arguments were: 1

As the final example suggests, `quotient`

can
only be applied
to integers. The `/`

procedure, on the other
hand, can be
applied to numbers of any kind (except that you can't use zero as a
divisor) and yields a single result:

>(/ 13 4)

13/4

>(/ 1 2.5)

0.4

The `modulo`

procedure is like `remainder`

,
except
that it always yields a result that has the same sign as the divisor.
In
particular, this means that when the divisor is positive and the
dividend
is negative, `modulo`

yields a positive (or
zero) result.

>(remainder -13 4)

-1

>(modulo -13 4)

3

There are five comparison predicates for comparing values,
`<`

(less than), `<=`

(less than or equal to), `=`

(equal to), `>=`

(greater than or equal to),
and `>`

(greater than). When given two
arguments, they return
`#t`

if the indicated relation holds between the
two arguments.

>(< 5 10)

#t

>(> 5 10)

#f

These predicates and also take more than two arguments. The predicate
returns `#t`

only if the relation holds between
each pair of adjacent
arguments.

The `log`

procedure, despite its name, computes
natural
(base e) logarithms rather than common (base ten) logarithms. You can
convert a natural logarithm into a common logarithm by dividing it by
the natural logarithm of 10. In case you've forgotten, the common
logarithm of *n* is the power to which you
raise 10 in order
to get

.
*n*

>(log 100)

4.605170185988092

>(/ (log 100) (log 10))

2.0

When using Scheme's trigonometric functions, which include
`sin`

, `cosine`

, and `tan`

,
bear in
mind that all angles are measured in radians, not degrees.

>(sin 90)

0.8939966636005579

>(cos 90)

-0.4480736161291701

>pi

3.141592653589793

>(exact? pi)

#f

>(sin (/ pi 2))

1.0

>(cos (/ pi 2))

6.123031769111886e-17

You may wonder why the cosine of pi-over-2 (a right angle) is not 0.
It's because `pi`

is not exactly the value of
pi. However,
as scientific notation indicates, the value is pretty close to 0.
(There
are sixteen leading 0's.)

Janet Davis (davisjan@cs.grinnell.edu)

Created August 31, 2006 based on http://www.cs.grinnell.edu/~rebelsky/Courses/CS151/2006F/Readings/numbers.htmlLast revised September 3, 2006

With thanks to Sam Rebelsky and John David Stone