Infinity symbol http://people.csail.mit.edu/jaffer/III/RAWI

Infinities in Scheme


Introduction

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.  

Background

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.  

Apparatus

Procedures

or

Method

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.  

If an error occurs while interactively running "inf.scm", the tests can be run manually:

string->number

Table 1: string->number of (real) infinity strings
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)
Notes:
Table 2a: string->number of complex infinity strings
Input Gambit 4.0
"inf.+i"
"-inf.+i" -inf.+i
"+nan.+i" +nan.+i
"-nan.+i"
"+inf.i" +inf.i
"-inf.i" -inf.i
"+nan.i" +nan.i
"-nan.i"
"0+inf.i" +inf.i
"0-inf.i" -inf.i
"0+nan.i" +nan.i
"0-nan.i"
"inf.+inf.i"
"inf.-inf.i"
"inf.+nan.i"
"inf.-nan.i"
"-inf.+inf.i" -inf.+inf.i
"-inf.-inf.i" -inf.-inf.i
"-inf.+nan.i" -inf.+nan.i
"-inf.-nan.i"
"+nan.+inf.i" +nan.+inf.i
"+nan.-inf.i" +nan.-inf.i
"+nan.+nan.i" +nan.+nan.i
"+nan.-nan.i"
"-nan.+inf.i"
"-nan.-inf.i"
"-nan.+nan.i"
"-nan.-nan.i"
"+1/0i" ... [g]
Input Mz Scheme 301
"inf.0+i"
"-inf.0+i" -inf.0+1.0i
"+nan.0+i" +nan.0+1.0i
"-nan.0+i" +nan.0+1.0i
"+inf.0i"
"-inf.0i"
"+nan.0i"
"-nan.0i"
"0+inf.0i" 0+inf.0i
"0-inf.0i" 0-inf.0i
"0+nan.0i" 0+nan.0i
"0-nan.0i" 0+nan.0i
"inf.0+inf.0i"
"inf.0-inf.0i"
"inf.0+nan.0i"
"inf.0-nan.0i"
"-inf.0+inf.0i" -inf.0+inf.0i
"-inf.0-inf.0i" -inf.0-inf.0i
"-inf.0+nan.0i" -inf.0+nan.0i
"-inf.0-nan.0i" -inf.0+nan.0i
"+nan.0+inf.0i" +nan.0+inf.0i
"+nan.0-inf.0i" +nan.0-inf.0i
"+nan.0+nan.0i" +nan.0+nan.0i
"+nan.0-nan.0i" +nan.0+nan.0i
"-nan.0+inf.0i" +nan.0+inf.0i
"-nan.0-inf.0i" +nan.0-inf.0i
"-nan.0+nan.0i" +nan.0+nan.0i
"-nan.0-nan.0i" +nan.0+nan.0i
Table 2b: string->number of complex infinity strings
Input SCM 5e2
"1/0+i" +inf.0
"-1/0+i" -inf.0
"0/0+i" 0/0
"+1/0i" 0/0
"-1/0i" 0/0
"+0/0i" 0/0
"-0/0i" 0/0
"1/0+1/0i" 0/0
"1/0-1/0i" 0/0
"1/0+0/0i"
"1/0-0/0i"
"-1/0+1/0i"0/0
"-1/0-1/0i"0/0
"-1/0+0/0i"
"-1/0-0/0i"
"0/0+1/0i" 0/0
"0/0-1/0i" 0/0
"0/0+0/0i"
"0/0-0/0i"
Input SCM 5e2
"#i1/0+i" +inf.0
"#i-1/0+i" -inf.0
"#i0/0+i" 0/0
"#i+1/0i" 0/0
"#i-1/0i" 0/0
"#i+0/0i" 0/0
"#i-0/0i" 0/0
"#i1/0+1/0i" 0/0
"#i1/0-1/0i" 0/0
"#i1/0+0/0i"
"#i1/0-0/0i"
"#i-1/0+1/0i"0/0
"#i-1/0-1/0i"0/0
"#i-1/0+0/0i"
"#i-1/0-0/0i"
"#i0/0+1/0i" 0/0
"#i0/0-1/0i" 0/0
"#i0/0+0/0i"
"#i0/0-0/0i"
Input Kawa 1.8
"1/0+i" 1/0+1i
"-1/0+i" -1/0+1i
"0/0+i"
"+1/0i" +1/0i
"-1/0i" -1/0i
"+0/0i"
"-0/0i"
"1/0+1/0i" 1/0+1/0i
"1/0-1/0i" 1/0-1/0i
"1/0+0/0i"
"1/0-0/0i"
"-1/0+1/0i"-1/0+1/0i
"-1/0-1/0i"-1/0-1/0i
"-1/0+0/0i"
"-1/0-0/0i"
"0/0+1/0i"
"0/0-1/0i"
"0/0+0/0i"
"0/0-0/0i"
Input Kawa 1.8
"#i1/0+i" #i1/0+1.0i
"#i-1/0+i" #i1/0+1.0i
"#i0/0+i" #i0/0+1.0i
"#i+1/0i" #i+1/0i
"#i-1/0i" #i+1/0i
"#i+0/0i" #i+0/0i
"#i-0/0i" #i+0/0i
"#i1/0+1/0i" #i1/0+1/0i
"#i1/0-1/0i" #i1/0-1/0i
"#i1/0+0/0i" #i1/0+0/0i
"#i1/0-0/0i" #i1/0+0/0i
"#i-1/0+1/0i" #i1/0+1/0i
"#i-1/0-1/0i" #i1/0-1/0i
"#i-1/0+0/0i"#i-1/0+0/0i
"#i-1/0-0/0i"#i-1/0+0/0i
"#i0/0+1/0i" #i0/0+1/0i
"#i0/0-1/0i" #i0/0-1/0i
"#i0/0+0/0i" #i0/0+0/0i
"#i0/0-0/0i" #i0/0+0/0i
[g]*** ERROR -- REAL expected
(make-rectangular #f 1)
Notes:

Reading and Writing Infinities

The floating-point string conversions in GNU glibc 2.2 support reading and writing of infinite values:

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:

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:

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.  

Division by zero

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.  

Table 3: Division by Zero
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 00
(/ -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.01/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
Table 4: Properties of special quotients
inexactexactrationalintegerrealpositivezeronegative
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.

Discussion

The glibc syntax for infinities has two undesirable aspects:

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.

Conclusion

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.

Copyright 2003, 2006 Aubrey Jaffer

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!