http://people.csail.mit.edu/jaffer/III/RAWI | |
Infinities in Scheme |
The three special values inf, -inf, and nan of IEEE Standard 754 are well-defined and integrated into 754's arithmetic system.
With nearly every Scheme implementation using IEEE floating-point numbers, we should consider advancing the language to reap more of the benefits of IEEE-754 by standardizing Scheme syntaxes for infinities. Doing so would also improve storage and communication of numerical data between programs.
This article contains research conducted for the SRFI-70 proposal for numbers in the next revision of The Algorithmic Language Scheme. It has been updated for SCM 5e2 which implements SRFI-70.
Checks for division by zero (and their absence) remain one of the most
common programming errors. Table 1,
for example, shows Scheme48 and MIT-Scheme signaling errors evaluating
(string->number "1/0")
.
Is it merely a coincidence that these implementations are the
least tolerant (among those tested) of division by zeros?
The realization that division by zero "works" lets initialization of variables precede bounds checks and gives flexibility in placement of those checks. Halting of programs need not occur in order to test limit and corner cases. Preserving infinities in data sets can free presentation programs from imposing arbitrary range limits.
Scheme interpreter or compiler.
The Scheme program "inf.scm"
Invoke Scheme interactively, then (load "inf.scm")
;
Edit the end of "inf.scm" to call trydivs
with or
without an argument as appropriate for the Scheme's
eval
. Then compile "inf.scm" and run.
real-infs
in "inf.scm" is a list of
all known (to me) literal representations of inf,
-inf, and nan.
complex-infs
in "inf.scm" is a list of the combinations
within each group of infinity representations in one or both
components of complex numbers.
The procedure str2num
prints its string argument, tries
to convert that string to a number, and writes out that number on the
same line if successful.
str2num
is called with each string in
real-infs
.
str2num
is called with each string in
complex-infs
.
The procedure trydivs
calls /
with all
possible combinations of one or two exact or inexact zeros,
printing each expression and its result.
The procedure inex-props
calls /
with
the combinations having inexact denominator "0.
" and
prints HTML rows detailing the numerical predicates applied to
the ratio.
If an error occurs while interactively running "inf.scm", the tests can be run manually:
(for-each str2num real-infs)
(for-each str2num complex-infs)
(trydivs (interaction-environment))
(inex-props (interaction-environment))
Input | glibc 2.2 strtod() | Gambit 4.0 | Chicken 1(89) | Guile 1.8.0 | Mz Scheme 301 | Chez Scheme 6.1 | Larceny 0.90 | SCM 5e2 | kawa 1.8 | Scheme 48 1.3 | MIT Scheme 7.8 |
---|---|---|---|---|---|---|---|---|---|---|---|
"+inf.0" | inf [2] | +inf.0 | +inf.0 | +inf.0 | +inf.0 | +inf.0 | |||||
"-inf.0" | -inf[2] | -inf.0 | -inf.0 | -inf.0 | -inf.0 | -inf.0 | |||||
"+nan.0" | nan [2] | +nan.0 | +nan.0 | +nan.0 | +nan.0 | ||||||
"-nan.0" | nan [2] | +nan.0 | +nan.0 | +nan.0 | +nan.0 | ||||||
"nan.0" | nan [2] | +nan.0 | |||||||||
"+inf." | inf [1] | +inf. | |||||||||
"-inf." | -inf[1] | -inf. | |||||||||
"+nan." | nan [1] | +nan. | |||||||||
"-nan." | nan [1] | ||||||||||
"nan." | nan [1] | ||||||||||
"+INF" | inf | Infinity. | |||||||||
"-Inf" | -inf | ||||||||||
"inf" | inf | Infinity. | |||||||||
"+NAN" | nan | NaN. | |||||||||
"-Nan" | nan | -NaN. | |||||||||
"nan" | nan | NaN. | |||||||||
"+#.#" | 0. | 5.5 | |||||||||
"-#.#" | -0. | -5.5 | |||||||||
"#.#" | |||||||||||
"1/0" | Infinity. | [g] | [s] | [s] | +inf.0 | 1/0 | [s] | ||||
"-1/0" | -Infinity. | [g] | [s] | [s] | -inf.0 | -1/0 | [s] | ||||
"0/0" | NaN. | [g] | [s] | [s] | 0/0 | [s] | |||||
"#i0/0" | [n] | NaN. | [g] | +nan.0 | [s] | [s] | 0/0 | #i0/0 | [s] |
[1], [2] | Characters left at end |
[s] | ;Division by zero signaled by /. |
[n] | *** ERROR -- NUMBER expected (exact->inexact #f) |
[g] | ABORT: (numerical-overflow) |
#f
was returned.
scanf()
reads identically with
strtod()
.
strtod()
also accepts "infinity", but
no substrings of it other than "inf".
string->number
does not accept "-nan."
.
string->number
accepts "-nan.0"
.
|
|
|
|
|
|
[g] | *** ERROR -- REAL expected (make-rectangular #f 1) |
#f
was returned.
string->number
accepts imaginary
infinities only with a real 0 part prepended,
eg. "0+inf.0i"
.
"#i-"
lose the minus
sign.
"#i"
prefixes.
The floating-point string conversions in GNU glibc 2.2 support reading and writing of infinite values:
inf
is positive infinity;
-inf
is negative infinity;
nan
is Not-a-Number.
IEEE 754 does not specify string representations for inf and nan. These names were apparently chosen to match 754 nomenclature.
Although not specified by the Revised5 Report on the Algorithmic Language Scheme, a few Scheme implementations support reading and literal use of their representations of infinities.
The richness of Scheme's syntax of numerical constants provides
several opportunities to support infinities. An idea which has
occurred to several authors independently is to use #
in
place of all digits in a number:
+#.#
is positive infinity;
-#.#
is negative; and
#.#
is NaN (not a number).
This notation is compact and would not be easily mistaken for any
other Scheme object. The problem arises when these objects are
components of complex numbers. The coefficient of i
must
be prefixed by a sign, making +#.#i
ambiguous.
Remediations like disallowing complex NaNs would be modifying
semantics in pursuit of syntax; not a good practice.
Gambit and MzScheme both use strings derived from IEEE-754 nomenclature:
+inf.
versus +inf.0
-inf.
versus -inf.0
+nan.
versus +nan.0
Requiring the leading sign character prevents confusion with identifiers, but "+inf" spoofs "+i". NaN, an intrinsically unsigned construct, is required to have a sign prefix by both systems. Gambit does not recognize "-nan." as NaN. MzScheme recognizes both "+nan.0" and "-nan.0" as NaN, but always prints "+nan.0".
SCM 5d8 printed infinities as rational numbers with denominators of "0". SCM's existing reader decodes this format without modification:
Kawa's infinities look like inexact rational numbers.  
Since an implementation is allowed to coerce the result of division by 0 to inexact, SCM always does so.
If one of these procedures is unable to deliver an exact result when given exact arguments, then it may either report a violation of an implementation restriction or it may silently coerce its result to an inexact number.
Table 3 catalogs the results of dividing exact
and inexact numbers by exact and inexact zeros. In both
MzScheme and MIT-Scheme (/ 0 0.)
returns
"0". An inexact "0." is
apparently weaker than an exact "0" in those
implementations.
quotient | SCM 5e2 | Kawa 1.8 | Bigloo 2.7a-r2 | glibc 2.2 printf | Larceny 0.90 | Guile 1.8.0 | Elk 3.0 | Gambit 4.0 | Mz Scheme 301 | Chez Scheme 6.1 | MIT Scheme 7.8 |
---|---|---|---|---|---|---|---|---|---|---|---|
(/ 0.) | +inf.0 | #i1/0 | +Infinity | inf | +inf.0 | +inf.0 | Inf.0 | +inf. | +inf.0 | +inf.0 | [t] |
(/ -1. 0.) | -inf.0 | #i-1/0 | -Infinity | -inf | -inf.0 | -inf.0 | -Inf.0 | -inf. | -inf.0 | -inf.0 | [t] |
(/ -1 0.) | -inf.0 | #i-1/0 | -Infinity | -inf | -inf.0 | -inf.0 | -Inf.0 | -inf. | -inf.0 | -inf.0 | [t] |
(/ 0. 0.) | 0/0 | #i0/0 | Infinity | nan | +nan.0 | +nan.0 | NaN | +nan. | +nan.0 | +nan.0 | [a] |
(/ 0 0.) | 0/0 | #i0/0 | Infinity | nan | +nan.0 | +nan.0 | NaN | +nan. | 0 | 0 | 0 |
(/ -1. 0) | -inf.0 | #i-1/0 | -Infinity | -inf | -inf.0 | [n] | [e] | [g] | [d] | [d] | [t] |
(/ 0. 0) | 0/0 | #i0/0 | Infinity | nan | +nan.0 | [n] | [e] | [g] | [d] | [d] | [a] |
(/ 0 0) | 0/0 | [j] | [b] | [c] | [d] | [n] | [e] | [g] | [d] | [d] | [s] |
(/ 0) | +inf.0 | 1/0 | [b] | [c] | [d] | [n] | [e] | [g] | [d] | [d] | [s] |
(/ -1 0) | -inf.0 | -1/0 | [b] | [c] | [d] | [n] | [e] | [g] | [d] | [d] | [s] |
quotient | Scheme 48 1.3 | Chicken 1(89) |
---|---|---|
(/ any 0.) | [r] | [d] |
(/ any 0) | [r] | [d] |
[a] | ;Anonymous arithmetic error |
[b] | *** ERROR:arithmetic procedure: `floating point' exception -- raised |
[c] | Floating point exception |
[d] | /: division by zero |
[e] | /: argument out of range: 0 |
[g] | *** ERROR -- Divide by zero |
[j] | java.lang.ArithmeticException: / by zero |
[n] | Numerical overflow |
[r] | Error: vm-exception |
[s] | ;Division by zero signaled by /. |
[t] | ;Division by zero |
inexact | exact | rational | integer | real | positive | zero | negative | ||
---|---|---|---|---|---|---|---|---|---|
Bigloo 2.7a-r2 | +Infinity | * | * | * | * | * | |||
-Infinity | * | * | * | * | * | ||||
Infinity | * | * | * | ||||||
Mz Scheme 301 | +inf.0 | * | * | * | * | * | |||
-inf.0 | * | * | * | * | * | ||||
+nan.0 | * | * | * | ||||||
Guile 1.8.0 | +inf.0 | * | * | * | * | * | |||
-inf.0 | * | * | * | * | * | ||||
+nan.0 | * | * | * | ||||||
Elk 3.0 | Inf.0 | * | * | * | * | * | |||
-Inf.0 | * | * | * | * | * | ||||
NaN | * | * | |||||||
Gambit 4.0 | +inf. | * | * | * | |||||
-inf. | * | * | * | ||||||
+nan. | * | * | |||||||
SCM 5e2 | +inf.0 | * | * | * | |||||
-inf.0 | * | * | * | ||||||
0/0 | * | [p] | [n] | ||||||
Kawa 1.8 | #i1/0 | * | * | * | |||||
#i-1/0 | * | * | * | ||||||
#i0/0 | * | * | |||||||
1/0 | * | * | * | * | |||||
-1/0 | * | * | * | * |
[p] | ;ERROR: positive?: Wrong type in arg1 0/0 |
[n] | ;ERROR: negative?: Wrong type in arg1 0/0 |
Table 4 shows the numerical properties of each of the special values. Only Kawa supports (2) exact infinities. For inexact infinities the only variations are in the real, rational, and integer columns.
The glibc syntax for infinities has two undesirable aspects:
Nothing visually distinguishes inf and nan from text words.
Inf and nan will be meaningless acronyms to people unfamiliar with IEEE-754 who encounter them in data files.
Gambit and MzScheme address the first problem by requiring a sign
prefix. Glibc reads signed infinities, so they would
interoperate in one direction. But both implementations
add suffixes to inf and nan which
strtod()
and scanf()
do not ignore.
The SRFI-70
discussions found general agreement on the +inf.0
and
-inf.0
syntaxes for real infinities.
This survey of infinities in Scheme shows general agreement among implementations that scalar infinities are inexact reals. Most allow infinities as independent real and imaginary components of complex numbers.
Several implementations provide read/write invariance of inexact
complex infinities; but their syntaxes are incompatible.
Implementations should adopt the syntaxes +inf.0
and
-inf.0
, which should also be incorporated into the next
Scheme standard.
I am a guest and not a member of the MIT Computer Science and Artificial Intelligence Laboratory.
My actions and comments do not reflect in any way on
MIT. | ||
Scheme | ||
agj @ alum.mit.edu | Go Figure! |