- Read By
- Wednesday, Sep 5, 2018
- Summary
- We examine a variety of issues pertaining to numeric values in Scheme, including the types of numbers that Scheme supports and some common numeric functions.

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, as you’ve seen with some of your initial experiments with images, a significant amount of computation involves numbers.

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.

As you probably learned in secondary school, there are a variety of kinds
of numbers. The most common types are the *integers* (numbers with no
fractional component), *rational numbers* (numbers that can be expressed
as the ratio of two integers), and *real numbers* (numbers that can
be plotted on a number line). Some Scheme implementations support only
integers and real numbers.

In some Scheme implementations, including those we will use in this
course, other numeric types are available, such as the *rational numbers*
(numbers that can be expressed as the ratio of two integers) and *complex
numbers* (numbers with a possible imaginary component). Why does Script-Fu
leave out some kinds of numbers? Because the implementers did not see
a need for them. In fact, the standard language definition for Scheme
says that an implementation of the language does not have to support
all categories of numbers.

Scheme provides two basic *predicates* that let us check whether or not
a value has a particular type: `integer?`

and `real?`

.

```
> (integer? 2)
#t
> (real? 2)
#t
> (integer? 2.5)
#f
> (real? 2.5)
#t
> (integer? "two")
#f
```

If an implementation of Scheme includes other types of numbers, it will
usually include predicates for those numbers. You will find that
DrRacket adds `rational?`

and `complex?`

.

We will return to these predicates, and others, when we consider conditionals.

Within each category 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?`

. Real
numbers are never represented exactly, and integers can be represented
exactly or inexactly. You can convert between the two representations with
`exact->inexact`

and `inexact->exact`

.

```
> (exact? 2)
#t
> (exact? 2.0)
#f
> (inexact->exact 2.0)
2
```

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.

All Scheme implementations support integers and reals. Racket the dialect of Scheme we use in this course, also supports rational numbers and complex numbers. Racket’s support of rational numbers may mean that you get results as a ratio of two numbers, rather than as a decimal number. For example, when you divided 2 by 5, you might expect to get 0.4, as in

```
> (/ 2 5)
0.4
```

In fact, you will see that result in many Scheme implementations. However, since decimals are often approximated, Racket prefers rationals when it makes sense to use them. Hence, in Racket , you’ll see slightly different output.

```
> (/ 2 5)
2/5
```

Most of the time, it won’t really matter which representation Scheme uses. However, there are times that the results are a bit confusing when expressed in rational form. You may have see these confusing result when computing averages, as in the following

```
> (/ (+ 5 3 2 4 3 5) 6)
11/3
```

Often, it helps to put these numbers into inexact form.

```
> (exact->inexact (/ (+ 5 3 2 4 3 5) 6))
3.6666666666666665
```

Racket provides two additional procedures for working with rational
numbers, `numerator`

and `denominator`

. As you might expect, these
return the numerator and denominator of a rational number.

```
> (numerator 5/7)
5
> (denominator 5/7)
7
> (numerator 20/6)
10
> (denominator 20/6)
3
```

It’s generally a bad idea to use these procedures with inexact numbers, as Racket may choose different values than most normal people expect.

```
> (numerator 0.4)
3602879701896397.0
> (denominator 0.4)
9007199254740992.0
> (/ (numerator 0.4) (denominator 0.4))
0.4
```

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. As
you read about procedures, think about how you might use them in writing
color filters or in other graphical algorithms.

*Warning! The output from different Scheme interpreters are inconsistent,
and sometimes even inconsistent with our expectations. In a few cases,
you may see slightly different responses than appear in this reading.*

As you’ve already seen, 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.

The `max`

procedure returns the largest of its parameters and the `min`

procedure returns the smallest of its parameters. As we’ve already seen,
`max`

can be useful when you want to ensure that a computation returns
a value no smaller than a certain value and `min`

can be useful when
you want to ensure that a computation returns a value no larger than a
desired maximum value.

There are four procedures that relate to division (`/`

, `quotient`

,
`remainder`

, and `modulo`

).

You’ve already seen that `/`

can divide one value by another. If you call
the `/`

procedure with a single parameter, it returns the multiplicative
inverse of that parameter (its reciprocal), the result of dividing 1
by it.

```
> (/ 2)
0.5
> (/ 1)
1
> (/ 0.5)
2
> (/ 0)
Error! /: division by zero
```

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: “Thirteen divided by four is three with a remainder of one”:

```
> (quotient 13 4)
3
> (remainder 13 4)
1
> (quotient 1 2.5)
Error! 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.

What about the little bit that’s left over after we use `quotient`

?
For example, the quotient of 42 and 8 is 5, but 8*5 is only 40. The
`remainder`

procedure gives that value. We may find some clever ways
to use `remainder`

throughout the semester.

```
> (quotient 42 8)
5
> (remainder 42 8)
2
```

The `modulo`

procedure is like `remainder`

, except that it always yields a
result that has the same sign as the divisor, whereas `remainder`

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

yields a
positive (or zero) result. (When can a remainder be negative? Consider
-7 divided by 3. Do we think of -7 as -2*3-1 or -3*3+2? Scheme makes
the former decision for remainder and the latter decision for modulo.)

```
> (remainder 13 4)
1
> (modulo 13 4)
1
> (remainder -13 4)
-1
> (modulo -13 4)
3
> (remainder 13 -4)
1
> (modulo 13 -4)
-3
> (remainder -13 -4)
-1
> (modulo -13 -4)
-1
```

We mention `modulo`

only because we use it at a few times throughout
the semester. We will re-explain it when we use it again. For now, you
can just think of it as “basically the same as remainder”, although,
as you’ll see from the following examples, it does not behave quite the
same way.

At times, we will have a real number and will want to convert it to a nearby integer. For example, we may have prices that inclulde cents but want to work only in whole-dollar amounts.

Scheme provides four basic procedures for this conversion: `round`

,
`truncate`

, `floor`

, and `ceiling`

. You will explore the differences
between these procedures in the corresponding lab.

*Warning!* At times, the Scheme interpreter will complain that it
is expecting an integer but sees a real value, even when you think
you have an integer. The problem is not with you, but with the error
messages. Most of the time that the interpreter says that it wants an
integer, it really wants an *exact* integer, so use `inexact->exact`

to get the number in the correct form.

Scheme provides five basic predicates for comparing numeric 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 can also take more than two arguments. Each predicate
returns `#t`

only if the relation holds between each pair of adjacent
arguments. If the relation fails to hold between a pair of adjacent
arguments, the predicate returns `#f`

.

```
> (< 2 3 4)
#t
> (< 2 3 1)
#f
```

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

Scheme provides the standard host of trigonometric functions, which
include `sin`

, `cosine`

, and `tan`

. When using these functions, remember
that all angles are measured in radians, not degrees.

```
> (sin 90)
0.8939966636005579
> (cos 90)
-0.4480736161291701
> pi
3.141592654
> (exact? pi)
#f
> (sin (/ pi 2))
1.0
> (cos (/ pi 2))
6.123031769e-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, but is rounded off. However,
as scientific notation indicates, the value is pretty close to 0. (There
are sixteen leading 0’s.)

We can use the trigonometric functions when we start doing more involved drawings. For example, they can help us draw polygons. The trigonometric functions also provide the opportunity to do some interesting color transformations.

As the reading suggests, there are two dimensions along which we can think about numbers. We can consider the kinds of values permitted (integer, rational, real, or complex) and we can consider the precision with which the numbers are represented.

Note that when we write numbers with a decimal point (e.g.,
`12.5`

or `2.0`

) we are telling the interpreter to use an *inexact*
representation. Note also that rational numbers can be represented in
standard fraction form, as in `23/4`

Identify an example of each of the following kinds of numbers. You may use DrRacket to verify your answers.

a. *An exact integer.* That is, something you can substitute into the underlined part of the following and get the given result.

```
> (exact? ___)
#t
> (integer? ___)
#t
```

b. *An inexact integer.* That is, something you can substitute into the underlined part of the following and get the given result.

```
> (inexact? ___)
#t
> (integer? ___)
#t
```

c. *An exact rational number that is not an integer.* That is, something you can substitute into the underlined part of the following and get the given result.

```
> (exact? ___)
#t
> (rational? ___)
#t
> (integer? ___)
#f
```

d. *An exact real number that is not an integer.* That is, something you can substitute into the underlined part of the following and get the given result.

```
> (exact? ___)
#t
> (real? ___)
#t
> (integer? ___)
#f
```

e. *An inexact real number that is not an integer.* That is, something you can substitute into the underlined part of the following and get the given result.

```
> (inexact? ___)
#t
> (real? ___)
#t
> (integer? ___)
#f
```

`modulo`

and `remainder`

Procedures, RevisitedMany students are puzzled by both the `modulo`

and `remainder`

procedures. For `remainder`

, you really should think back to middle-school
math: the remainder is what’s left after whole-number division. Since
`modulo`

is the same as `remainder`

for positive numbers, you can think
of it that way.

If you want to know more, read on. If not, you can stop here.

More importantly, `modulo`

provides an interesting way of counting. Most
of the time you add 1, you follow standard protocols (1 plus 1 is 2,
2 plus 1 is 3, …). However, when you reach the modulus value, you go
back to zero.

You can also think of the value of `(modulo value modulus)`

as follows: We break the number line up into `modulus`

-sized sections and then find the offset of `value`

from the start of that section. For example, if we use a modulus of 10, the non-negative sections of the number line would be (0..9), (10..19), (20..29), and so on and so forth. The number 23 would be in the section (20..29). Since it’s 3 bigger than 20, `(modulo 23 10)`

is 3.

The following table shows the value of `remainder`

and `modulo`

for a
variety of values.

n | -4 | -3 | -2 | -1 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|

`(remainder n 3)` |
-1 | 0 | -2 | -1 | 0 | 1 | 2 | 0 | 1 | 2 | 0 | 1 | 2 |

`(remainder n 4)` |
0 | -3 | -2 | -1 | 0 | 1 | 2 | 3 | 0 | 1 | 2 | 3 | 0 |

`(modulo n 3)` |
2 | 0 | 1 | 2 | 0 | 1 | 2 | 0 | 1 | 2 | 0 | 1 | 2 |

`(modulo n 4)` |
0 | 1 | 2 | 3 | 0 | 1 | 2 | 3 | 0 | 1 | 2 | 3 | 0 |

`(modulo n 5)` |
1 | 2 | 3 | 4 | 0 | 1 | 2 | 3 | 4 | 0 | 1 | 2 | 3 |

`(modulo n -3)` |
-1 | 0 | -2 | -1 | 0 | -2 | -1 | 0 | -2 | -1 | 0 | -2 | -1 |

`(modulo n -4)` |
0 | -3 | -2 | -1 | 0 | -3 | -2 | -1 | 0 | -3 | -2 | -1 | 0 |

`(remainder n -3)` |
-1 | 0 | -2 | -1 | 0 | 1 | 2 | 0 | 1 | 2 | 0 | 1 | 2 |

`(remainder n -4)` |
0 | -3 | -2 | -1 | 0 | 1 | 2 | 3 | 0 | 1 | 2 | 3 | 0 |