[comp.lang.functional] do computers believe in real numbers?

aet@felix.ee.mu.OZ.AU (Albert Edward THOMPSON) (03/28/91)

	I am trying to implement real numbers in a normal-order functional language--
NOT floating point numbers or any other approximation.
	This is my problem:
		I don't know what real numbers are 
		or even whether they can exist in any pragmatic (i.e. computational) sense.

	I looked at Bishop's 'Constructive Analysis' but could work out how to 
construct only rational reals. 
	I've heard about some things called the computable reals.
(some sort of countable subset of the reals?)
What are they? Are there reals which are not computable?
If so, in what way do they "exist"?

Concretely, my picture of real arithmetic on computer is something like this:
user>    sqrt 2
machine>   answer1    ;machine gives answer in unforced form.
user> answer1 5       ;user asks to see answer in explicit form accurate to 5 decimal places.
machine> 1.41421      ;machine forces answer as asked for.
user> log pi
machine> answer2
user> answer2 8
machine> 1.14472988

	It is not important whether the machine rounds off (which it probably must do anyway)
	or gives the "true" digits in the decimal expansion.
	(although this is theoretically interesting -- comments welcome!)
	What is important is that the machine should do much work only when digits are 
	explicitly requested and that the machine ideally should give _any_ accuracy.

	Perhaps some symbolic maths language uses reals rather than approximations?

Sorry if I'm being nebulous or ill-informed:
I still don't know exactly what is required in this problem.

		Preemptive thanks for any suggestions,

												Bert.

edgar@function.mps.ohio-state.edu (Gerald Edgar) (03/28/91)

In article <7197@munnari.oz.au> aet@felix.ee.mu.OZ.AU (Albert Edward THOMPSON) writes:
>
> I am trying to implement real numbers in a normal-order functional language--

There is a "calculator" for Suns that does this.  It cites these references:

Hans-J. Boehm, "Constructive Real Interpretation of Numerical Programs",
Proceedings of the SIGPLAN '87 Symposium on Interpreters and Interpretive
Techniques, SIGPLAN Notices 22, 7 (July 1987), pp. 241-221.

Hans-J. Boehm, R. Cartwright, Michael J. O'Donnell, and Mark Riggle,
"Exact Real Arithmetic: A Case Study in Higher Order Programming",
Proceedings of the 1986 Lisp and Functional Programming Conference,
pp. 162-173.

Brent, R.P., "Fast Multiple-Precision Evaluation of Elementary Functions",
Journal of the ACM 23, (1976), pp. 242-251.
--
  Gerald A. Edgar          
  Department of Mathematics             Bitnet:    EDGAR@OHSTPY
  The Ohio State University             Internet:  edgar@mps.ohio-state.edu
  Columbus, OH 43210   ...!{att,pyramid}!osu-cis!shape.mps.ohio-state.edu!edgar

straub@jogger.cs.umd.edu (Pablo A. Straub) (03/29/91)

In article <7197@munnari.oz.au> aet@felix.ee.mu.OZ.AU (Albert Edward THOMPSON)
writes:
>I am trying to implement real numbers in a normal-order functional language--
>NOT floating point numbers or any other approximation.
>This is my problem:
>  I don't know what real numbers are 
>  or even whether they can exist in any pragmatic (i.e. computational) sense.

You may want to look at

	Hans Bohem and Robert Cartwright
	Exact real numbers: Formulating real numbers as functions
	In Research Topics in Functional Programming
	Edited by David A. Turner
	University of Texas at Austin Year of Programming Series
	Addison Wesley 1990
----
Pablo A. Straub, straub@cs.umd.edu

boehm@parc.xerox.com (Hans Boehm) (03/29/91)

Another more recent, but less general, reference is "Optimizing Programs over
the Constructive Reals", PLDI '90, (appeared as SIGPLAN Notices 25, 6), by
Vernon Lee and myself.

The calculator now runs on a variety of UNIX workstations.  It can be obtained
by anonymous ftp from arisia.xerox.com:~ftp/pub/C_calc.tar.Z.  This is mostly
automatically generated C code.  Send mail if you want the real source.

Hans-J. Boehm
(boehm@xerox.com)

fs@caesar.cs.tulane.edu (Frank Silbermann) (04/03/91)

In article <91090.154205NN1@awiwuw11.wu-wien.ac.at> NN1@awiwuw11.wu-wien.ac.at writes:
>	there are several equivalent ways to define the notion
>	of computable real number.
>
>	A number a is computable, if the n-th digit of a is
>	a recursive function of n, or, equivalently,
>	if the set of all rational numbers <= a
>	is recursive (assuming some coding scheme for rational numbers).
>	...
>	Now for the more practical quesiton raised by A.E. Thomson,
>	how real numbers should indeed be represented on a computer,
>	so that at any stage one can get as many digits as requested,
>	...
>Heinrich Rolletschek
>k310670@AEARN

One difficulty with this definition would be the problem
of asking for the first 5 mantissa digits of an expression
that simplifies to:
	                                              _
	0.099999999999999999999999999999999999999999999 (9's forever).

Clearly, the result equals 0.1 exactly,
so the first five mantissa digits OUGHT to be 0.10000,
yet, most naive systems would report 0.09999 !!!
If computation with real numbers is to be "computable,"
we need a more lenient requirement than
"ability to compute any single digit in a finite amount of time,"
and we need a more lenient notion of approximation than
"truncation of the digits."

The problem is that, in mathematics, equality is defined
via convergence of infinite chains of approximations,
and _not_ by the ability to produce identical normal forms.

[MORAL:	This should be a lesson to those who believe
	functional programming should be based on some
	arbitrary lambda calculus or rewriting system.
	It should be based on domain theory,
	which takes into account the existence of
	infinite chains of approximation.

	Also, consider the fact that mathematics
	has not given us any single notation
	suitable for expressing _every possible_
	computable function over the reals (or even the rationals).
	This may be a hint that we should not worry too much
	if our functional programming languages are not "fully abstract,"
	that our domain contains objects which cannot be denoted
	by any expression in our language.
]
-- 
	Frank Silbermann	fs@cs.tulane.edu
	Tulane University	New Orleans, Louisiana  USA
--
	Frank Silbermann	fs@cs.tulane.edu
	Tulane University	New Orleans, Louisiana  USA

boehm@parc.xerox.com (Hans Boehm) (04/03/91)

Aberth makes the observation however that if instead of asking for the first n
digits in the infinite expansion, I ask for the best rounded n digit
approximation (where I'm counting digits to the right of the decimal point),
and I occasionally allow the machine to give me the best n+1 digit rounded
approximation instead, then the result is computable, provided I can
approximate the number to an arbitrarily small tolerance, i.e. I'm starting
with a recursive real number.  I simply evaluate the number to a tolerance of
less than one in the n+2nd digit.  If the n+1st and n+2nd digits are 5 and 0,
then I print the first n digits followed by a 5.  In all other cases, I can
safely round to n digits.  (This is of course not optimal.)  I can also
ask for the first n truncated digits if I use a redundant notation, e.g.
if I allow digits -9 through -1 as well.  Of course that gets hard to read ...

Hans
(boehm@xerox.com)

rjc@cstr.ed.ac.uk (Richard Caley) (04/03/91)

In article <6878@rex.cs.tulane.edu>, Frank Silbermann (fs) writes:

fs> Also, consider the fact that mathematics
fs> has not given us any single notation
fs> suitable for expressing _every possible_
fs> computable function over the reals (or even the rationals).

Apologies if I am being slow, but if a function is computable does
that not imply that it can be denoted in some notational scheme which
someone has decided is a reasonable model of `computation' (e.g. TMs,
Markov Algorithms or whatever). 

If `computable' is deleted from the sentance quoted it becomes an
obvious tautology, so I must be missing something.

BTW: has anyone mentioned Boehm & Cartwright's paper in `Research
	Topics in Functional Programming'? They argue that the reals
	should be represnted as functions not as digit sequences for
	efficiancy reasons(!). (full reference on demand, I don't have
	it with me).

--
rjc@cstr.ed.ac.uk	Wandered in from sci.lang to ask dumb questions.

suarez@prl.dec.com (Ascander Suarez) (04/04/91)

In article <7197@munnari.oz.au>, aet@felix.ee.mu.OZ.AU (Albert Edward THOMPSON) writes:
  
  	I am trying to implement real numbers in a normal-order functional language--
  NOT floating point numbers or any other approximation.
  	This is my problem:
  		I don't know what real numbers are 
  		or even whether they can exist in any pragmatic (i.e. computational) sense.
  
  ...

There is an article at the lisp  and Functional Programming conference 1988 entitled:

Exact Real Computer Arithmetic with Continued Fractions
by Jean Vuillemin

The author presents a representation of the computable real numbers and 
algorithms for their manipulation.

	Ascander Suarez
	suarez@decprl.dec.com

jgk@osc.COM (Joe Keane) (04/05/91)

We can characterise constructive real numbers by the primitive operations that
are provided on them.  For example, one way to describe the numbers in [0,2[
is by the following operations:
	return the integer part of a given real (0 or 1)
	return twice the fractional part of a given real (another real)
	make a new real given its integer part and twice its fractional part

It should be clear that these operations are all that is needed.  Given them,
you can write functions to add two numbers, and so on.  I have restricted the
numbers to [0,2[ for simplicity, but i trust you will see that this isn't a
problem.

These operations suggest a simple implementation.  We can represent a real
number by a (possibly lazy) list of 0 or 1.  Then the two operations are just
car, cdr, and cons on the representation.

Here is another set of operations which can define real numbers:
	return the integer part of a given real
	return the reciprocal of the fractional part of a given real
	make a new real given its integer part and the reciprocal of its
		fractional part

These operations suggest a different simple implementation.  We can represent
a real number by a list of integers which is its continued fraction expansion.
Again the operations are just car, cdr, and cons.

There's no reason for us to think that either or these sets of operations (or
some other) is the `right' one to define real numbers.  Rather, given an
implementation which directly supports a set of operations powerful enough to
define the reals, we can implement the other operations in terms of these.
For a given implementation, some operations are primitive (they work directly
on the representation) and some are generic, built from primitive operations.

Now we can code up any number of implementations, and if they support the
basic operations, they will work together.  That is, we can add together a
number in binary form and one in continued fraction form without any problem.

Although these operations seem intuitive, there's a problem with them.  They
are too strict, because they require exact tests.  For example taking the
integer part leaves no room for error: floor(0.9999) = 0 while floor(1.0000) =
1.  So in place of a strict test like x > 0, we can have an operation like
this:
	given x return either x < 1 or x > -1
Note that the results overlap.  By allowing a choice of return values for some
range of arguments, we eliminate the problems of strict tests.

A similar operation is the following:
	given x and a positive number epsilon, return either
		x < 0, x > 0, or |x| < epsilon
In this case we can provide a tolerance as small as we want, but not zero.  Of
course by providing a smaller tolerance we can expect the test to take longer.
I hope you see that these operations are equivalent, assuming we also have
some simple ones like `double a number'.

Tests like this suggest a redundant number representation.  It's been
recognized since Turing that redundant representations are the way to go.
With a non-redundant representation, even such a simple thing as adding two
numbers can require you to look at an unbounded number of digits of the
arguments.  In contrast, a redundant representation allows you to generate the
result `on the fly' with a minimal (bounded) amount of look-ahead.

Of course people want to see numbers in decimal or test if they're positive,
so we have to convert to a non-redundant form some time.  But by delaying this
as long as possible, we eliminate most of the problems with things looking too
far ahead.  When there is such a problem, it's because the user asked a
question which is hard to answer, and not because we did something wrong.

So how do you implement these things?  Well, all you have to do is pick the
right set of combinators, and algorithms to go with them.  Of course this
makes it sound simple.  Really a lot of problems (compiling programs for
example) can be reduced to ``pick the right set of combinators'' (plus
algorithms to use them of course).  That may give some insight, but it doesn't
necessarily make it easy.
--
Joe Keane, supercombinator hacker
jgk@osc.com (...!uunet!stratus!osc!jgk)

johnsson@cs.chalmers.se (Thomas Johnsson) (04/05/91)

In article <1991Apr4.095619.27416@prl.dec.com> suarez@prl.dec.com (Ascander Suarez) writes:
>There is an article at the lisp  and Functional Programming conference 1988 entitled:
>
>Exact Real Computer Arithmetic with Continued Fractions
>by Jean Vuillemin
>
>The author presents a representation of the computable real numbers and 
>algorithms for their manipulation.

This paper also appeared recently in IEEE Transactions on Computers,
which I think i a more detailed version.

Thomas Johnsson  (johnsson@cs.chalmers.se)
Dept. of CS, Chalmers University of Technology, S-412 96 Goteborg, Sweden
phone: dept: +46 (0)31 721088.

tom@nw.stl.stc.co.uk (04/05/91)

digits in the infinite expansion, I ask for the best rounded n digit
approximation (where I'm counting digits to the right of the decimal point),
and I occasionally allow the machine to give me the best n+1 digit rounded
approximation instead, then the result is computable, provided I can
approximate the number to an arbitrarily small tolerance, i.e. I'm starting
with a recursive real number.  I simply evaluate the number to a tolerance of
less than one in the n+2nd digit.  If the n+1st and n+2nd digits are 5 and 0,
then I print the first n digits followed by a 5.  In all other cases, I can
safely round to n digits.  (This is of course not optimal.)  I can also
ask for the first n truncated digits if I use a redundant notation, e.g.
if I allow digits -9 through -1 as well.  Of course that gets hard to read ...


======
 Stuff and nonsense!  How do you know when you are close enough?  A recursive
real number is simply a function f:N->R satisfying a convergence constraint (and
of course being recursive too) [R=2 * N * (N-{0}), ie the rationals]. I don't
see how you can use the convergence constraint to know when your approximation
is close enough to be right to so many decimal places (suppose f is identically
zero on the first twenty milliion naturals,  and 1 thereafter: how do you know
after looking at 10 million terms that you still don't have the first digit?
how do you know after looking at the first 50 million terms that you do now h
the first digit?)

fs@rex.cs.tulane.edu (Frank Silbermann) (04/08/91)

>> Also, consider the fact that mathematics
>> has not given us any single notation
>> suitable for expressing _every possible_
>> computable function over the reals (or even the rationals).

In article <RJC.91Apr3095056@brodie.cstr.ed.ac.uk> rjc@cstr.ed.ac.uk (Richard Caley) writes:
>	Apologies if I am being slow, but if a function is computable,
>	does that not imply that it can be denoted in some notational
>	scheme which someone has decided is a reasonable model
>	of `computation' (e.g. TMs, Markov Algorithms or whatever). 

A Turing-complete formalism can _model_ the computation
of any computable function.  That does not mean that every
such formalism can _directly express_ every computable function.
For instance, you can model any parallel computation
with a sequential Turing machine, but you cannot
using a sequential Turing machine to _directly express_
parallel computation.

Most mathematicians working with real arithmentic
care only that their ad-hoc notations communicate the concepts
and relations (e.g. functions) that interest them at the moment.
Most do not concern themselves that their notation
is complete -- i.e. that they have set up a language
suitable for expressing every possible computable function over the reals.
When the topic of discourse changes, the notation is enriched as needed.

When trying to design a functional programming language
to work with objects of a given semantic domain,
I analogously argue that we ought not require
that the function from syntax to semantic domain
(expressed by the denotational equations) be "onto",
i.e. that full abstraction is a luxury we cannot always afford.
(This has nothing to do with the language's Turing equivalence.)

	Frank Silbermann	fs@cs.tulane.edu
	Tulane University	New Orleans, Louisiana  USA

aet@mullian.ee.mu.OZ.AU (Albert Edward THOMPSON) (04/09/91)

	It seems androids dream of computable reals.

>Article 742 of comp.lang.functional:
>Subject: Re: do computers believe in real numbers?
>Summary: They do, but languages tend not to.
>Keywords: redundant representation
>Reply-To: jgk@osc.COM (Joe Keane)

	I'm the bloke who posted the query on real numbers.
I found your posting very interesting,
but there're a few bits i'm not sure about.

>We can characterise constructive real numbers by the primitive operations that
>are provided on them.  For example, one way to describe the numbers in [0,2[
>is by the following operations:
>	return the integer part of a given real (0 or 1)
>	return twice the fractional part of a given real (another real)
>	make a new real given its integer part and twice its fractional part
>
>It should be clear that these operations are all that is needed.  Given them,
>you can write functions to add two numbers, and so on.  I have restricted the
>numbers to [0,2[ for simplicity, but i trust you will see that this isn't a
>problem.

	integerPart: |R -> {0,1}
	twiceFractionPart: |R -> |R
	makeReal: {0,1} x |R -> |R

	Wouldn't you need another function to create a real without starting out
with a real in the first place? i.e. no |R on the left of the ->
Otherwise this would not be an abstract data type and would be sort of 
a circular definition.
	By the way, how do you define functions like sin, cos, log, etc?
The series definitions are infinite. 
	In fact, how do you define higher-order functions like
derivative, integral, fourierTransform?
	(I already know the analysis (supposedly :^), but how do you deal with
	infinite series, limits, etc. in a concrete way?
	Is there something else that has to be included in the abstract data type
	to take care of this?)

	Hey! Your suggestions about lazy continued fraction representation and
redundancy in representation sound like the basis of quite a workable 
implementation.

		Thanks for a very useful posting!

										Bert.