Title

Preliminary Proposal for R6RS Arithmetic

Authors

William D Clinger and Michael Sperber

Status

This SRFI is currently in ``withdrawn'' status. For an explanation of each status that a SRFI can hold, see here. You can access the discussion via the archive of the mailing list.

This SRFI is being submitted by members of the Scheme Language Editor's Committee as part of the R6RS Scheme standardization process. The purpose of such ``R6RS SRFIs'' is to inform the Scheme community of features and design ideas under consideration by the editors and to allow the community to give the editors some direct feedback that will be considered during the design process.

At the end of the discussion period, this SRFI will be withdrawn. When the R6RS specification is finalized, the SRFI may be revised to conform to the R6RS specification and then resubmitted with the intent to finalize it. This procedure aims to avoid the situation where this SRFI is inconsistent with R6RS. An inconsistency between R6RS and this SRFI could confuse some users. Moreover it could pose implementation problems for R6RS compliant Scheme systems that aim to support this SRFI. Note that departures from the SRFI specification by the Scheme Language Editor's Committee may occur due to other design constraints, such as design consistency with other features that are not under discussion as SRFIs.

Table of contents

Abstract

Scheme's arithmetic system was designed to allow a wide variety of implementations. After many years of implementation experience, however, most implementations now fall into a small number of categories, and the benefits of continued experimentation no longer justify the confusion and portability problems that have resulted from giving implementations so much freedom in this area. Moreover, the R5RS generic arithmetic is difficult to implement as efficiently as purely fixnum or purely flonum arithmetic. (Fixnum arithmetic is typically limited-precision integer arithmetic implemented using one or more representations that may be especially efficient on the executing machine; flonum arithmetic is typically limited-precision floating-point arithmetic using one or more representations that may be especially efficient on the executing machine.)

This SRFI is an effort to extend and clarify the R5RS arithmetic to make it more portable, more comprehensive, and enable faster programs.

Furthermore, one of us (Sperber) has argued that Scheme's arithmetic requires radical overhaul. The other (Clinger) agrees that revisions are needed. Whether these revisions qualify as radical is best left to the judgement of individual readers.

This SRFI proposes to revise section 6.2 ("Numbers") of R5RS by:

Issues

Members of the Scheme community are encouraged to express themselves on the following issues:

Revision History

Rationale

Most implementations of Scheme fall into one of the following categories:

Under R5RS, it is hard to write programs whose arithmetic is portable across the above categories, and it is unnecessarily difficult even to write programs whose arithmetic is portable between different implementations in the same category.

The portability problems can most easily be solved by requiring all implementations to support the full numeric tower. To prevent that requirement from making Scheme substantially more difficult to implement, we provide a reference implementation that constructs the full numeric tower from a fixnum/flonum base. To ensure the portability of such reference implementations, the fixnum/flonum base must be described and (at least partially) standardized.

Fixnum/flonum arithmetic is already supported by many systems, mainly for efficiency. Standardization of fixnum/flonum arithmetic would increase the portability of code that uses it, but we cannot standardize the precision of fixnum/flonum arithmetic without making it inefficient on some systems, which would defeat its purpose. We therefore propose to specify the syntax and much of the semantics of fixnum/flonum arithmetic, but to make the precision a parameter of the specification.

Several details of R5RS are inconsistent or incomplete with respect to the IEEE standards for binary floating point arithmetic, which are generally used to implement Scheme's inexact real arithmetic. Furthermore, some details of R5RS make it unnecessarily difficult to implement Scheme's arithmetic efficiently.

This SRFI is incompatible with SRFI 70 [Jaffer 2005] in several ways, including:

Both of the above differences are motivated by closure properties that make it easier for an implementation to generate efficient numerical code.

Moreover, unlike SRFI 70, this alternative operations for integer division div, mod, div0, and mod0, which are slightly more generally applicable and easier to specify. It also does not extend the gcd and lcm to rational numbers.

The sections on Fixnums, Flonums, Exact Arithmetic, and Inexact Arithmetic are new in the SRFI and not in SRFI 70. This SRFI also omits SRFI 70's discussion of infinities and offers its own.

Other, more minor differences with SRFI 70 are highlighted in the hypertext. (The sections mentioned in the previous paragraph are not marked up, as they would be (almost in the case of generic exact arithmetic) completely red.

Specification

We assume that Section 6.2.3 ("Implementation restrictions") of R5RS remains essentially as it stands. The text of this SRFI describes the differences between the proposed additions and changes to R5RS and/or SRFI 70.

The R6RS is expected to describe some kind of library facility, but this SRFI does not rely on that feature. We expect some of the new arithmetic procedures specified by this SRFI will be available only within certain libraries.

Infinities and NaNs

Positive infinity is regarded as a real (but not rational) number, whose value is indeterminate but greater than all rational numbers. Negative infinity is regarded as a real (but not rational) number, whose value is indeterminate but less than all rational numbers.

A NaN is regarded as a real (but not rational) number whose value is so indeterminate that it might represent any real number, including positive or negative infinity, and might even be greater than positive infinity or less than negative infinity.

This SRFI is written as though infinities and NaNs are representable, and specifies many operations with respect to these numbers in ways that are consistent with the IEEE 754 standard for binary floating point arithmetic. Although implementations of Scheme are not required to represent infinities and NaNs, an implementation must report a violation of an implementation restriction whenever it is unable to represent an infinity or NaN as required by the specification below. This requirement also applies to conversions between numbers and external representations, including the reading of program source code.

Note: We expect the R6RS to refine this specification in enough detail so programs can specify exception handlers that recover from these particular violations of implementation restrictions by substituting alternatives values for the unrepresentable infinity or NaN.

External Representations

This SRFI adds the following external representations to Scheme:

Implementations that use binary floating point representations of real numbers should represent x|p using a p-bit significand if practical, or by a greater precision if a p-bit significand is not practical, or by the largest available precision if p or more bits of significand is not practical within the implementation.

Note: The precision of a significand should not be confused with the number of bits used to represent the significand. In the IEEE floating point standards, for example, the significand's most significant bit is implicit in single and double precision but is explicit in extended precision. Whether that bit is implicit or explicit does not affect the mathematical precision. In implementations that use binary floating point, the default precision can be calculated by calling the following procedure:

    (define (precision)
      (do ((n 0 (+ n 1))
           (x 1.0 (/ x 2.0)))
        ((= 1.0 (+ 1.0 x)) n)))

Note: When the underlying floating-point representation is IEEE double precision, the |p suffix should not be omitted for all cases: Denormalized numbers have diminished precision, and therefore should carry a |p suffix with the actual width of the significand.

The number->string procedure is generalized over the R5RS version to support the x|p notation.

To be consistent with this SRFI, the write procedure would be required to write inexact numbers in the external representation produced by number->string with one argument.

Implementations are not required to use floating-point representations, but every implementation is required to designate a subset of its inexact reals as flonums, and to convert certain external representations into flonums.

The R6RS section on the lexical structure of numerical tokens should read as as follows:

The following rules for <num R>, <complex R>, <real R>, <ureal R>, <uinteger R>, and <prefix R> should be replicated for R = 2, 8, 10, and 16. There are no rules for <decimal 2>, <decimal 8>, and <decimal 16>, which means that numbers containing decimal points or exponents or mantissa widths must be in decimal radix.

<num R>  --> <prefix R> <complex R>
<complex R> --> <real R> | <real R> @ <real R>
    | <real R> + <ureal R> i | <real R> - <ureal R> i
    | <real R> + i | <real R> - i
    | + <ureal R> i | - <ureal R> i | + i | - i
<real R> --> <sign> <ureal R>
<ureal R>  -->  <uinteger R>
    |  <uinteger R> / <uinteger R>
    |  <decimal R> <mantissa width>
    |  inf.0 | nan.0
<decimal 10>  -->  <uinteger 10> <suffix>
    |  . <digit 10>+ #* <suffix>
    |  <digit 10>+ . <digit 10>* #* <suffix>
    |  <digit 10>+ #* . #* <suffix>
<uinteger R>  -->  <digit R>+ #*
<prefix R>  -->  <radix R> <exactness>
    |  <exactness> <radix R>

<suffix>  -->  <empty>
    |  <exponent marker> <sign> <digit 10>+
<exponent marker>  -->  e  |  s  |  f  |  d  |  l
<mantissa width> -> <empty>
    | | <digit 10>+
<sign>  -->  <empty>  |  +  |  -
<exactness>  -->  <empty>  |  #i  |  #e
<radix 2>  -->  #b
<radix 8>  -->  #o
<radix 10>  -->  <empty>  |  #d
<radix 16>  -->  #x
<digit 2>  -->  0  |  1
<digit 8>  -->  0  |  1  |  2  |  3  |  4  |  5  |  6  |  7
<digit 10>  -->  0  |  1  |  2  |  3  |  4  |  5  |  6  |  7  |  8  |  9
<digit 16>  -->  <digit 10>  |  a  |  b  |  c  |  d  |  e  |  f

If a <decimal 10> does not contain a non-empty <mantissa width> and does not contain one of the exponent markers s, f, d, or l, but does contain a decimal point or the exponent marker e, then it is an external representation for a flonum. Furthermore inf.0, +inf.0, -inf.0, nan.0, +nan.0, and -nan.0 are external representations for flonums. Some or all of the other external representations for inexact reals may also represent flonums, but that is not required by this SRFI.

If a <decimal 10> contains a non-empty <mantissa width> or one of the exponent markers s, f, d, or l, then it represents an inexact number, but does not necessarily represent a flonum.

Safe and Unsafe Mode

The R6RS is expected to require implementations to provide a "safe" mode in which specified exceptional situations must be detected and handled in standard ways, while allowing (but not requiring) implementations to support an "unsafe" mode that is not guaranteed to detect those situations or to handle them in the standard way even when detected. These modes may interact with many features of Scheme, but are particularly relevant to the fixnum and flonum operations. In safe mode, these operations must check that their arguments are actually fixnums or flonums respectively, or perform possible additional checking as required by the specifications of the operations. In unsafe mode, these operations are not required to perform such checking. This distinction allows programmers to request more efficient numerical code for those operations at the cost of less effective run-time checking.

This SRFI uses the phrase "all bets are off" to describe situations in which the behavior of a procedure is unspecified when executing in unsafe mode. Specifically, a procedure call for which all bets are off is free to crash the system.

Equivalence of Objects

The R6RS specification of eqv? for numbers should be changed as follows.

The eqv? procedure returns #t if:

The eqv? procedure returns #f if:

Numerical Type Predicates

The following description is a revised form of the description given in SRFI 70. Changes from SRFI 70 are highlighted in red.

procedure: number? obj
procedure: complex? obj
procedure: real? obj
procedure: rational? obj
procedure: integer? obj

These numerical type predicates can be applied to any kind of argument, including non-numbers. They return #t if the object is of the named type, and otherwise they return #f. In general, if a type predicate is true of a number then all higher type predicates are also true of that number. Consequently, if a type predicate is false of a number, then all lower type predicates are also false of that number.

If z is an inexact a complex number, then `(real? z)' is true if and only if `(zero? (imag-part z))' and `(exact? (imag-part z))' are both true.

If x is a real number, then `(rational? x)' is true if and only if there exist exact integers k1 and k2 such that `(= x (/ k1 k2))' and `(= (numerator x) k1)' and `(= (denominator x) k2)' are all true. Thus infinities and NaNs are not rational numbers.

If x is an inexact real number, then `(integer? x)' is true if and only if
    (and (finite? x) (= x (round x)))
If q is a rational number, then `(integer? q)' is true if and only if `(= (denominator q) 1)' is true. If q is not a rational number, then `(integer? q)' is false.
(complex? 3+4i)                        ==>  #t
(complex? 3)                           ==>  #t
(real? 3)                              ==>  #t
(real? -2.5+0.0i)                      ==>  #f
(real? -2.5+0i)                        ==>  #t
(real? -2.5)                           ==>  #t
(real? #e1e10)                         ==>  #t
(rational? 6/10)                       ==>  #t
(rational? 6/3)                        ==>  #t
(rational? 2)                          ==>  #t
(integer? 3+0i)                        ==>  #t
(integer? 3.0)                         ==>  #t
(integer? 8/4)                         ==>  #t

(number? +nan.0)                       ==>  #t
(complex? +nan.0)                      ==>  #t
(real? +nan.0)                         ==>  #t
(rational? +nan.0)                     ==>  #f
(complex? +inf.0)                      ==>  #t
(real? -inf.0)                         ==>  #t
(rational? -inf.0)                     ==>  #f
(integer? -inf.0)                      ==>  #f

Note: In many implementations the rational? procedure will be the same as real?, and the complex? procedure will be the same as number?, but unusual implementations may be able to represent some irrational numbers exactly or may extend the number system to support some kind of non-complex numbers.

Note: The behavior of these type predicates on inexact numbers is unreliable, because any inaccuracy may affect the result.

procedure: real-valued? obj
procedure: rational-valued? obj
procedure: integer-valued? obj

These numerical type predicates can be applied to any kind of argument, including non-numbers. They return #t if the object is a number and is equal in the sense of = to some object of the named type, and otherwise they return #f.

(real-valued? +nan.0)                  ==>  #f
(real-valued? -inf.0)                  ==>  #t
(real-valued? 3)                       ==>  #t
(real-valued? -2.5+0.0i)               ==>  #t
(real-valued? -2.5+0i)                 ==>  #t
(real-valued? -2.5)                    ==>  #t
(real-valued? #e1e10)                  ==>  #t

(rational-valued? +nan.0)              ==>  #f
(rational-valued? -inf.0)              ==>  #f
(rational-valued? 6/10)                ==>  #t
(rational-valued? 6/10+0.0i)           ==>  #t
(rational-valued? 6/10+0i)             ==>  #t
(rational-valued? 6/3)                 ==>  #t

(integer-valued? 3+0i)                 ==>  #t
(integer-valued? 3+0.0i)               ==>  #t
(integer-valued? 3.0)                  ==>  #t
(integer-valued? 3.0+0.0i)             ==>  #t
(integer-valued? 8/4)                  ==>  #t

Note: The behavior of these type predicates on inexact numbers is unreliable, because any inaccuracy may affect the result.

procedure: exact? z
procedure: inexact? z

These numerical predicates provide tests for the exactness of a quantity. For any Scheme number, precisely one of these predicates is true.

(exact? 5)                   ==>  #t
(inexact? +inf.0)            ==>  #t

Generic Conversions

procedure: exact->inexact z
procedure: inexact->exact z

Exact->inexact returns an inexact representation of z. If inexact numbers of the appropriate type have bounded precision, then the value returned is an inexact number that is nearest to the argument. If an exact argument has no reasonably close inexact equivalent, then a violation of an implementation restriction may be reported.

Inexact->exact returns an exact representation of z. The value returned is the exact number that is numerically closest to the argument; in most cases, the result of this procedure should be numerically equal to its argument. If an inexact argument has no reasonably close exact equivalent, then a violation of an implementation restriction may be reported.

These procedures implement the natural one-to-one correspondence between exact and inexact integers throughout an implementation-dependent range.

Exact->inexact and inexact->exact are idempotent.

procedure: real->flonum x

Real->flonum returns a flonum representation of x, which must be a real number. The value returned is a flonum that is numerically closest to the argument.

Rationale: The flonums are a subset of the inexact reals, but may be a proper subset. The real->flonum procedure converts an arbitrary real to the flonum type required by flonum-specific procedures.

Note: If flonums are represented in binary floating point, then implementations are strongly encouraged to break ties by preferring the floating point representation whose least significant bit is zero.

Numerical Input and Output

procedure: number->string z
procedure: number->string z radix
procedure: number->string z radix precision

Radix must be an exact integer, either 2, 8, 10, or 16. If omitted, radix defaults to 10. If a precision is specified, then z must be an inexact complex number, precision must be an exact positive integer, and the radix must be 10. The procedure number->string takes a number and a radix and returns as a string an external representation of the given number in the given radix such that

    (let ((number number)
          (radix radix))
      (eqv? number
            (string->number (number->string number
                                            radix)
                            radix)))

is true. It is an error if no possible result makes this expression true.

If a precision is specified, then the representations of the inexact real components of the result, unless they are infinite or NaN, specify an explicit <mantissa width> p, and p is the least pprecision for which the above expression is true.

If z is inexact, the radix is 10, and the above expression and condition can be satisfied by a result that contains a decimal point, then the result contains a decimal point and is expressed using the minimum number of digits (exclusive of exponent, trailing zeroes, and mantissa width) needed to make the above expression and condition true [Burger, Dybvig 1996; Clinger 1990]; otherwise the format of the result is unspecified.

The result returned by number->string never contains an explicit radix prefix.

Note: The error case can occur only when z is not a complex number or is a complex number with a non-rational real or imaginary part.

Rationale: If z is an inexact number represented using binary floating point, and the radix is 10, then the above expression is normally satisfied by a result containing a decimal point. The unspecified case allows for infinities, NaNs, and representations other than binary floating point.

procedure: string->number string
procedure: string->number string radix

Returns a number of the maximally precise representation expressed by the given string. Radix must be an exact integer, either 2, 8, 10, or 16. If supplied, radix is a default radix that may be overridden by an explicit radix prefix in string (e.g. "#o177"). If radix is not supplied, then the default radix is 10. If string is not a syntactically valid notation for a number, then string->number returns #f.

(string->number "100")                 ==>  100
(string->number "100" 16)              ==>  256
(string->number "1e2")                 ==>  100.0
(string->number "15##")                ==>  1500.0
(string->number "+inf.0")              ==>  +inf.0
(string->number "-inf.0")              ==>  -inf.0
(string->number "+nan.0")              ==>  +nan.0

Note: The domain of string->number may be restricted by implementations in the following ways. String->number is permitted to return #f whenever string contains an explicit radix prefix. If all numbers supported by an implementation are real, then string->number is permitted to return #f whenever string uses the polar or rectangular notations for complex numbers. If all numbers are integers, then string->number may return #f whenever the fractional notation is used. If all numbers are exact, then string->number may return #f whenever an exponent marker or explicit exactness prefix is used, or if a # appears in place of a digit. If all inexact numbers are integers, then string->number may return #f whenever a decimal point is used. An implementation may return #f for "+nan.0".

Integer Division

For various kinds of arithmetic (fixnum, flonum, exact, inexact, and generic), this SRFI provides operations for performing integer division. They rely on mathematical operations div, mod, div0, and mod0, that are defined as follows:

div, mod, div0, and mod0 each accept two real numbers x1 and x2 as operands, where x2 must be nonzero.

div returns an integer, mod returns a real. Their results are specified by

x1 div x2         ==> nd
x1 mod x2         ==> xm
where
  1. x1 = nd * x2 + xm
  2. 0 <= xm < |x2|

Examples:

5 div 3    =  1
5 div -3   =  -1

5 mod 3    =  2
5 mod -3   =  2

Div0 and mod0 are like div and mod, except the result of mod0 lies within a half-open interval centered on zero. The results are specified by

x1 div0 x2        ==> nd
x1 mod0 x2        ==> xm
where:
  1. x1 = nd * x2 + xm
  2. -|x2/2| ≤ xm < |x2/2|

Examples:

5 div 3    =  2
5 div -3   =  -2

5 mod 3    =  -1
5 mod -3   =  -1

Rationale: The half-open symmetry about zero is convenient for some purposes.

Fixnums

A subrange of the exact integers is designated as the set of fixnums. Conversely, a fixnum is an exact integer whose value lies within this fixnum range.

Every implementation must define its fixnum range as a closed interval [-2w-1, 2w-1 - 1] such that w is a a (mathematical) integer w ≥ 24. Every mathematical integer within an implementation's fixnum range must correspond to an exact integer that is representable within the implementation.

This section specifies two kinds of operations on fixnums. Operations whose names begin with fixnum perform arithmetic modulo 2w. Operations whose names begin with fx perform integer arithmetic on their fixnum arguments, but signal an error if the result is not a fixnum.

Rationale: The operations whose names begin with fixnum implement arithmetic on a quotient ring of the integers, but their results will not be the same in every implementation because the particular ring is parameterized by w. The operations whose names begin with fx do not have as nice a closure property, and the arguments that cause them to signal an error will not be the same in every implementation, but any results they return without signalling an error will be the same in all implementations.

Some operations (e.g. fixnum< and fx<) behave the same in both sets.

Rationale: Duplication of names reduces bias toward either set, and saves programmers from having to remember which names are supplied.

We will use fx, fx1 and fx2 as metavariables that range over fixnums.

If an argument to the following procedures that corresponds to a fixnum metavariable is not actually a fixnum, then these procedures signal an error, unless the implementation is running in unsafe mode, in which case all bets are off.

procedure: fixnum? obj

This returns #t if obj is an exact integer within the fixnum range, and otherwise returns #f.

procedure: fixnum-width
procedure: least-fixnum
procedure: greatest-fixnum

These procedures return w, -2w-1 and 2w-1 - 1, the width, minimum and the maximum value of the fixnum range, respectively.

procedure: fixnum= fx1 fx2 fx3 ...
procedure: fixnum> fx1 fx2 fx3 ...
procedure: fixnum< fx1 fx2 fx3 ...
procedure: fixnum>= fx1 fx2 fx3 ...
procedure: fixnum<= fx1 fx2 fx3 ...
procedure: fx= fx1 fx2 fx3 ...
procedure: fx> fx1 fx2 fx3 ...
procedure: fx< fx1 fx2 fx3 ...
procedure: fx>= fx1 fx2 fx3 ...
procedure: fx<= fx1 fx2 fx3 ...

These procedures return #t if their arguments are (respectively): equal, monotonically increasing, monotonically decreasing, monotonically nondecreasing, or monotonically nonincreasing, #f otherwise.

procedure: fixnum-zero? fx
procedure: fixnum-positive? fx
procedure: fixnum-negative? fx
procedure: fixnum-odd? fx
procedure: fixnum-even? fx
procedure: fxzero? fx
procedure: fxpositive? fx
procedure: fxnegative? fx
procedure: fxodd? fx
procedure: fxeven? fx

These numerical predicates test a fixnum for a particular property, returning #t or #f. The five properties tested by these procedures are: whether the number is zero, greater than zero, less than zero, odd, or even.

procedure: fixnum-max fx1 fx2 ...
procedure: fixnum-min fx1 fx2 ...
procedure: fxmax fx1 fx2 ...
procedure: fxmin fx1 fx2 ...

These procedures return the maximum or minimum of their arguments.

procedure: fixnum+ fx1 ...
procedure: fixnum* fx1 ...

These procedures return the unique fixnum that is congruent mod 2w to the sum or product of their arguments.

procedure: fx+ fx1 fx2
procedure: fx* fx1 fx2

These procedures return the sum or product of their arguments, provided that sum or product is a fixnum. An error is signalled if that sum or product is not a fixnum, unless the implementation is running in unsafe mode, in which case all bets are off.

Rationale: These procedures are restricted to two arguments because their generalizations to three or more arguments would require precision proportional to the number of arguments.

procedure: fixnum- fx1 fx2 ...
procedure: fixnum- fx

With two or more arguments, this procedure returns the unique fixnum that is congruent mod 2w to the difference of its arguments, associating to the left. With one argument, however, it returns the the unique fixnum that is congruent mod 2w to the additive inverse of its argument.

procedure: fx- fx1 fx2
procedure: fx- fx

With two arguments, this procedure returns the difference of its arguments, provided that difference is a fixnum.

With one argument, this procedure returns the additive inverse of its argument, provided that integer is a fixnum.

An error is signalled if the mathematically correct result of this procedure is not a fixnum.

(fx- (least-fixnum))  ==>  error

procedure: fixnum-div+mod fx1 fx2
procedure: fixnum-div fx1 fx2
procedure: fixnum-mod fx1 fx2
procedure: fixnum-div0+mod0 fx1 fx2
procedure: fixnum-div0 fx1 fx2
procedure: fixnum-mod0 fx1 fx2

These procedures implement number-theoretic integer division modulo 2w. Each procedure returns the unique fixnum(s) congruent modulo 2w to the result(s) specified in the section on Integer Division. An error is signalled if the second argument is zero, unless the implementation is running in unsafe mode, in which case all bets are off.

(fixnum-div x1 x2)         ==> x1 div x2
(fixnum-mod x1 x2)         ==> x1 mod x2
(fixnum-div+mod x1 x2)     ==> x1 div x2, x1 mod x2 ; two return values
(fixnum-div0 x1 x2)        ==> x1 div0 x2
(fixnum-mod0 x1 x2)        ==> x1 mod0 x2
(fixnum-div0+mod0 x1 x2)   ==> x1 div0 x2, x1 mod0 x2 ; two return values

procedure: fxdiv+mod fx1 fx2
procedure: fxdiv fx1 fx2
procedure: fxmod fx1 fx2
procedure: fxdiv0+mod0 fx1 fx2
procedure: fxdiv0 fx1 fx2
procedure: fxmod0 fx1 fx2

These procedures implement number-theoretic integer division and return the results of the corresponding mathematical operations specified in the section on Integer Division. An error is signalled if a result specified by that section is not a fixnum, unless the implementation is running in unsafe mode, in which case all bets are off. An error is signalled if the second argument is zero, unless the implementation is running in unsafe mode, in which case all bets are off.

(fxdiv x1 x2)         ==> x1 div x2
(fxmod x1 x2)         ==> x1 mod x2
(fxdiv+mod x1 x2)     ==> x1 div x2, x1 mod x2 ; two return values
(fxdiv0 x1 x2)        ==> x1 div0 x2
(fxmod0 x1 x2)        ==> x1 mod0 x2
(fxdiv0+mod0 x1 x2)   ==> x1 div0 x2, x1 mod0 x2 ; two return values

procedure: fixnum+/carry fx1 fx2 fx3

Returns the two fixnum results of the following computation:

    (let* ((s (+ fx1 fx2 fx3))
           (s0 (mod0 s (expt 2 (fixnum-width))))
           (s1 (div0 s (expt 2 (fixnum-width)))))
      (values s0 s1))

Note: The results returned by the fixnum+/carry, fixnum-/carry, and fixnum*/carry procedures depend upon the precision w, so there are no fx equivalents to these procedures.

procedure: fixnum-/carry fx1 fx2 fx3

Returns the two fixnum results of the following computation:

    (let* ((d (- fx1 fx2 fx3))
           (d0 (mod0 d (expt 2 (fixnum-width))))
           (d1 (div0 d (expt 2 (fixnum-width)))))
      (values d0 d1))

procedure: fixnum*/carry fx1 fx2 fx3

Returns the two fixnum results of the following computation:

    (let* ((s (+ (* fx1 fx2) fx3))
           (s0 (mod0 s (expt 2 (fixnum-width))))
           (s1 (div0 s (expt 2 (fixnum-width)))))
      (values s0 s1))

procedure: fixnum-not fx
procedure: fxnot fx

Both of these procedures return the unique fixnum that is congruent mod 2w to the one's-complement of their argument.

procedure: fixnum-and fx1 ...
procedure: fixnum-ior fx1 ...
procedure: fixnum-xor fx1 ...
procedure: fxand fx1 ...
procedure: fxior fx1 ...
procedure: fxxor fx1 ...

These procedures return the fixnum that is the bit-wise "and", "inclusive or", or "exclusive or" of the two's complement representations of their arguments. If they are passed only one argument, they return that argument. If they are passed no arguments, they return the fixnum (either -1 or 0) that acts as identity for the operation.

procedure: fixnum-if fx1 fx2 fx3
procedure: fxif fx1 fx2 fx3

These procedures return the fixnum result of the following computation:

    (fixnum-ior (fixnum-and fx1 fx2)
                (fixnum-and (fixnum-not fx1) fx3))

procedure: fixnum-bit-count fx
procedure: fxbit-count fx

If the argument fx is non-negative, these procedures return the number of 1 bits in the two's complement representation of fx. Otherwise they return the number of 0 bits in the two's complement representation of fx.

procedure: fixnum-length fx
procedure: fxlength fx

These procedures return the fixnum result of the following computation:

    (do ((result 0 (+ result 1))
         (bits (if (fixnum-negative? fx)
                   (fixnum- fx)
                   fx)
               (fixnum-logical-shift-right bits 1)))
        ((fixnum-zero? bits)
         result))

procedure: fixnum-first-bit-set fx
procedure: fxfirst-bit-set fx

These procedures return the index of the least significant 1 bit in the two's complement representation of their argument. If the argument is 0, then -1 is returned.

(fixnum-first-bit-set 0)        ==>  -1
(fixnum-first-bit-set 1)        ==>  0
(fixnum-first-bit-set -4)       ==>  2

procedure: fixnum-bit-set? fx1 fx2

If the second argument is non-negative, this procedure returns the fixnum result of the following computation:

    (not (fixnum-zero?
          (fixnum-and fx1
                      (fixnum-logical-shift-left 1 fx2))))
If the second argument is negative, then an error is signalled, unless the implementation is running in unsafe mode, in which case all bets are off.

procedure: fxbit-set? fx fx2

If the first argument is negative, or greater than or equal to (fixnum-width), then an error is signalled, unless the implementation is running in unsafe mode, in which case all bets are off. Otherwise this procedure returns the same result returned by fixnum-bit-set?.

procedure: fixnum-copy-bit fx1 fx2 fx3

If the second argument is non-negative, then this procedure returns the fixnum result of the following computation:

    (let* ((mask (fixnum-logical-shift-left 1 fx2)))
      (fixnum-if mask
                 (fixnum-logical-shift-left fx3 fx2)
                 fx2))
If the second argument is negative, then an error is signalled, unless the implementation is running in unsafe mode, in which case all bets are off.

procedure: fxcopy-bit fx1 fx2 fx3

If the second argument is negative, or greater than or equal to (fixnum-width), or the third argument is anything other than 0 or 1, then an error is signalled, unless the implementation is running in unsafe mode, in which case all bets are off. Otherwise this procedure returns the same result returned by fixnum-copy-bit.

procedure: fixnum-bit-field fx1 fx2 fx3

If the second and third arguments are non-negative, this procedure returns the fixnum result of the following computation:

    (let* ((mask (fixnum-not
                  (fixnum-logical-shift-left -1 fx3))))
      (fixnum-logical-shift-right (fixnum-and fx1 mask)
                                  fx2))
If the second or third argument is negative, then an error is signalled, unless the implementation is running in unsafe mode, in which case all bets are off.

procedure: fxbit-field fx1 fx2 fx3

If the second or third argument is negative, or greater than (fixnum-width), or the second argument is greater than the third, then an error is signalled, unless the implementation is running in unsafe mode, in which case all bets are off. Otherwise this procedure returns the same result returned by fixnum-bit-field.

procedure: fixnum-copy-bit-field fx1 fx2 fx3 fx4

If the second and third arguments are non-negative, this procedure returns the fixnum result of the following computation:

    (let* ((to    fx1)
           (start fx2)
           (end   fx3)
           (from  fx4)
           (mask1 (fixnum-logical-shift-left -1 start))
           (mask2 (fixnum-not
                   (fixnum-logical-shift-left -1 end)))
           (mask (fixnum-and mask1 mask2)))
      (fixnum-if mask from to))
If the second or third argument is negative, then an error is signalled, unless the implementation is running in unsafe mode, in which case all bets are off.

procedure: fxcopy-bit-field fx1 fx2 fx3 fx4

If the second or third argument is negative, or greater than (fixnum-width), or the second argument is greater than the third, then an error is signalled, unless the implementation is running in unsafe mode, in which case all bets are off. Otherwise this procedure returns the same result returned by fixnum-copy-bit-field.

procedure: fixnum-arithmetic-shift fx1 fx2

Returns the unique fixnum that is congruent mod 2w to the result of the following computation:

    (exact-floor (* fx1 (expt 2 fx2)))

procedure: fxarithmetic-shift fx1 fx2

If the absolute value of the second argument is greater than or equal to (fixnum-width), then an error is signalled, unless the implementation is running in unsafe mode, in which case all bets are off. If

    (exact-floor (* fx1 (expt 2 fx2)))
is a fixnum, then that fixnum is returned. Otherwise an error is signalled, unless the implementation is running in unsafe mode, in which case all bets are off.

procedure: fixnum-arithmetic-shift-left fx1 fx2
procedure: fixnum-arithmetic-shift-right fx1 fx2

If the second argument is non-negative, then fixnum-arithmetic-shift-left returns the same result as fixnum-arithmetic-shift, and (fixnum-arithmetic-shift-right fx1 fx2) returns the same result as (fixnum-arithmetic-shift fx1 (fixnum- fx2)). If the second argument is negative, then an error is signalled, unless the implementation is running in unsafe mode, in which case all bets are off.

procedure: fxarithmetic-shift-left fx1 fx2
procedure: fxarithmetic-shift-right fx1 fx2

If the second argument is non-negative, then fxarithmetic-shift-left behaves the same as fxarithmetic-shift, and (fxarithmetic-shift-right fx1 fx2) behaves the same as (fxarithmetic-shift fx1 (fixnum- fx2)). If the second argument is negative, then an error is signalled, unless the implementation is running in unsafe mode, in which case all bets are off.

procedure: fixnum-logical-shift-left fx1 fx2

This procedure behaves the same as fixnum-arithmetic-shift-left.

procedure: fixnum-logical-shift-right fx1 fx2

If the second argument is non-negative, then this procedure returns the result of the following computation:

    (let* ((n       fx1)
           (shift   fx2)
           (shifted (fixnum-arithmetic-shift-right n shift)))
      (let* ((mask-width (fixnum- (fixnum-width)
                                  (fixnum-mod shift (fixnum-width))))
             (mask (fixnum-not
                    (fixnum-logical-shift-left -1 mask-width))))
        (fixnum-and shifted mask))
If the second argument is negative, then an error is signalled, unless the implementation is running in unsafe mode, in which case all bets are off.

Note: The results of fixnum-logical-shift-left and fixnum-logical-shift-left can depend upon the precision w, so they have no fx equivalents.

procedure: fixnum-rotate-bit-field fx1 fx2 fx3 fx4

If the second, third, and fourth arguments are non-negative, this procedure returns the result of the following computation:

    (let* ((n     fx1)
           (start fx2)
           (end   fx3)
           (count fx4)
           (width (fixnum- end start)))
      (if (fixnum-positive? width)
          (let* ((count (fixnum-mod count width))
                 (field0 (fixnum-bit-field n start end))
                 (field1 (fixnum-logical-shift-left field0 count))
                 (field2 (fixnum-logical-shift-right field0
                                                     (fixnum- width count)))
                 (field (fixnum-ior field1 field2)))
            (fixnum-copy-bit-field n field start end))
          n))

procedure: fxrotate-bit-field fx1 fx2 fx3 fx4

If the second, third, or fourth argument is negative, or greater than (fixnum-width), or the fourth argument is greater than or equal to the difference between the third and second arguments, then an error is signalled, unless the implementation is running in unsafe mode, in which case all bets are off. Otherwise this procedure returns the same result as the fixnum-rotate-bit-field procedure.

procedure: fixnum-reverse-bit-field fx1 fx2 fx3

Returns the fixnum obtained from the first argument by reversing the bit field specified by the second and third arguments.

(fixnum-reverse-bit-field #b1010010 1 4)    ==>  88 ; #b1011000
(fixnum-reverse-bit-field #b1010010 91 -4)  ==>  82 ; #b1010010

procedure: fxreverse-bit-field fx1 fx2 fx3

If the second or third argument is negative, or greater than (fixnum-width), or the second argument is greater than the third, then an error is signalled, unless the implementation is running in unsafe mode, in which case all bets are off. Otherwise this procedure returns the same result as the fixnum-reverse-bit-field procedure.

Flonums

Implementations are not required to use floating-point representations, but every implementation is required to designate a subset of its inexact reals as flonums, and to convert certain external representations into flonums. The flonums must be closed under the flonum operations described in this section.

We will use fl, fl1 and fl2 as metavariables that range over flonums, and ifl, ifl1 and ifl2 as metavariables that range over integer-valued flonums, i.e. flonums for which the integer-valued? predicate is true.

If an argument to the following procedures that corresponds to a (integral) flonum metavariable is not actually a (integral) flonum, then these procedures signal an error, unless the implementation is running in unsafe mode, in which case all bets are off.

procedure: flonum? obj

This returns #t if obj is a flonum, and otherwise returns #f.

procedure: fl= fl1 fl2 fl3 ...
procedure: fl< fl1 fl2 fl3 ...
procedure: fl<= fl1 fl2 fl3 ...
procedure: fl> fl1 fl2 fl3 ...
procedure: fl>= fl1 fl2 fl3 ...

These procedures return #t if their arguments are (respectively): equal, monotonically increasing, monotonically decreasing, monotonically nondecreasing, or monotonically nonincreasing, #f otherwise. These predicates are required to be transitive.

(fl= +inf.0 +inf.0)           ==>  #t
(fl= -inf.0 +inf.0)           ==>  #f
(fl= -inf.0 -inf.0)           ==>  #t
(fl= 0.0 -0.0)                ==>  #t
(fl< 0.0 -0.0)                ==>  #f
(fl= +nan.0 fl)               ==>  #f
(fl< +nan.0 fl)               ==>  #f

procedure: flinteger? fl
procedure: flzero? fl
procedure: flpositive? fl
procedure: flnegative? fl
procedure: flodd? ifl
procedure: fleven? ifl
procedure: flfinite? fl
procedure: flinfinite? fl
procedure: flnan? fl

These numerical predicates test a flonum for a particular property, returning #t or #f. Flinteger? tests it if the number is an integer, flzero? tests if it is fl= to zero, flpositive? tests if it is greater than zero, flnegative? tests if it is less than zero, flodd? tests if it is odd, fleven? tests if it is even, flfinite? tests if it is not an infinity and not a NaN, flinfinite? tests if it is an infinity, flnan? tests if it is a NaN.

(flnegative? -0.0)   ==> #f
(flfinite? +inf.0)   ==> #f
(flfinite? 5.0)      ==> #t
(flinfinite? 5.0)    ==> #f
(flinfinite? +inf.0) ==> #t

Note: `(flnegative? -0.0)' must return #f, else it would lose the correspondence with (fl< -0.0 0.0), which is #f according to the IEEE standards.

procedure: flmax fl1 fl2 ...
procedure: flmin fl1 fl2 ...

These procedures return the maximum or minimum of their arguments.

procedure: fl+ fl1 ...
procedure: fl* fl1 ...

These procedures return the flonum sum or product of their flonum arguments. In general, they should return the flonum that best approximates the mathematical sum or product. (For implementations that represent flonums as IEEE binary floating point numbers, the meaning of "best" is reasonably well-defined by the IEEE standards.)

(fl+ +inf.0 -inf.0)      ==>  +nan.0
(fl+ +nan.0 fl)          ==>  +nan.0
(fl* +nan.0 fl)          ==>  +nan.0

procedure: fl- fl1 fl2 ...
procedure: fl- fl
procedure: fl/ fl1 fl2 ...
procedure: fl/ fl

With two or more arguments, these procedures return the flonum difference or quotient of their flonum arguments, associating to the left. With one argument, however, they return the additive or multiplicative flonum inverse of their argument. In general, they should return the flonum that best approximates the mathematical difference or quotient. (For implementations that represent flonums as IEEE binary floating point numbers, the meaning of "best" is reasonably well-defined by the IEEE standards.)

(fl- +inf.0 +inf.0)      ==>  +nan.0

For undefined quotients, fl/ behaves as specified by the IEEE standards:

(fl/ 1.0 0.0)  ==> +inf.0
(fl/ -1.0 0.0) ==> -inf.0
(fl/ 0.0 0.0)  ==> +nan.0

procedure: flabs fl

This returns the absolute value of its argument.

procedure: fldiv+mod fl1 fl2
procedure: fldiv fl1 fl2
procedure: flmod fl1 fl2
procedure: fldiv0+mod0 fl1 fl2
procedure: fldiv0 fl1 fl2
procedure: flmod0 fl1 fl2

These procedures implement number-theoretic integer division and return the results of the corresponding mathematical operations specified in the section on Integer Division. For zero divisors, these procedures may return a NaN or some meaningless flonum.

(fldiv x1 x2)         ==> x1 div x2
(flmod x1 x2)         ==> x1 mod x2
(fldiv+mod x1 x2)     ==> x1 div x2, x1 mod x2 ; two return values
(fldiv0 x1 x2)        ==> x1 div0 x2
(flmod0 x1 x2)        ==> x1 mod0 x2
(fldiv0+mod0 x1 x2)   ==> x1 div0 x2, x1 mod0 x2 ; two return values

procedure: flnumerator fl
procedure: fldenominator fl

These procedures return the numerator or denominator of their argument as a flonum; the result is computed as if the argument was represented as a fraction in lowest terms. The denominator is always positive. The denominator of 0 is defined to be 1.

(flnumerator +inf.0)           ==>  +inf.0
(flnumerator -inf.0)           ==>  -inf.0
(fldenominator +inf.0)         ==>  1.0
(fldenominator -inf.0)         ==>  1.0
(flnumerator 0.75)             ==>  3.0 ; example
(fldenominator 0.75)           ==>  4.0 ; example
The following behavior is strongly recommended but not required by R6RS:
(flnumerator -0.0)             ==> -0.0

procedure: flfloor fl
procedure: flceiling fl
procedure: fltruncate fl
procedure: flround fl

These procedures return integral flonums for flonum arguments that are not infinities or NaNs. For such arguments, flfloor returns the largest integral flonum not larger than fl. Flceiling returns the smallest integral flonum not smaller than fl. Fltruncate returns the integral flonum closest to fl whose absolute value is not larger than the absolute value of fl. Flround returns the closest integral flonum to fl, rounding to even when fl is halfway between two integers.

Rationale: Flround rounds to even for consistency with the default rounding mode specified by the IEEE floating point standard.

Although infinities and NaNs are not integers, these procedures return an infinity when given an infinity as an argument, and a NaN when given a NaN:

(flfloor +inf.0)                       ==>  +inf.0
(flceiling -inf.0)                     ==>  -inf.0
(fltruncate +nan.0)                    ==>  +nan.0

procedure: flexp fl
procedure: fllog fl
procedure: flsin fl
procedure: flcos fl
procedure: fltan fl
procedure: flasin fl
procedure: flatan fl
procedure: flatan fl1 fl2

These procedures compute the usual transcendental functions. Fllog computes the natural logarithm of fl (not the base ten logarithm). Flasin, flacos, and flatan compute arcsine (sin-1), arccosine (cos-1), and arctangent (tan-1), respectively. `(flatan fl1 fl2)' computes the arc tangent of fl1/fl2. The range of flatan lies between -pi/2 and pi/2, both inclusive if -0.0 is distinguished, both exclusive otherwise.

In general, the mathematical functions log, arcsine, arccosine, and arctangent are multiply defined. The value of log z is defined to be the one whose imaginary part lies in the range from -pi (exclusive) to pi (inclusive). With log defined this way, the values of sin-1 fl, cos-1 z, and tan-1 z are according to the following formulae:

sin-1 z = -i log (i z + sqrt(1 - z2))

cos-1 z = pi / 2 - sin-1 z

tan-1 z = (log (1 + i z) - log (1 - i z)) / (2 i)

If the function has a real-valued limit as its argument tends toward positive infinity, then that is the value returned by the function applied to +inf.0. If the function has a real-valued limit as its argument tends toward negative infinity, then that is the value returned by the function applied to -inf.0.

In the event that these formulae do not yield a real result for the given arguments, the result may be a NaN, or may be some meaningless flonum.

Implementations that use IEEE binary floating point arithmetic are encouraged to follow the relevant standards for these procedures.

Specifically, the range of `(flatan x y)' is as in the following table. The asterisk (*) indicates that the entry applies to implementations that distinguish minus zero.

y Condition x Condition Range of result
y = 0.0 x > 0.0 0.0
* y = +0.0 x > 0.0 +0.0
* y = -0.0 x > 0.0 -0.0
y > 0.0 x > 0.0 0.0 < result< pi/2
y > 0.0 x = 0.0 pi/2
y > 0.0 x < 0.0 pi/2 < result< pi
y = 0.0 x < 0 pi
* y = +0.0 x < 0.0 +pi
* y = -0.0 x < 0.0 -pi
y < 0.0 x < 0.0 -pi< result< -pi/2
y < 0.0 x = 0.0 -pi/2
y < 0.0 x > 0.0 -pi/2 < result< 0.0
y = 0.0 x = 0.0 undefined
* y = +0.0 x = +0.0 +0.0
* y = -0.0 x = +0.0 -0.0
* y = +0.0 x = -0.0 +pi
* y = -0.0 x = -0.0 -pi

The above specification follows the branch cut specification of Common Lisp in [Pitman 1996], and [Steele 1990], which in turn cites [Penfield 1981]; refer to these sources for more detailed discussion of branch cuts, boundary conditions, and implementation of these functions.

(flexp +inf.0)                ==> +inf.0
(flexp -inf.0)                ==> 0.0
(fllog +inf.0)                ==> +inf.0
(fllog 0.0)                   ==> -inf.0
(fllog -0.0)                  ==> unspecified ; if -0.0 is distinguished
(fllog -inf.0)                ==> +nan.0
(flatan -inf.0)               ==> -1.5707963267948965 ; approximately
(flatan +inf.0)               ==> 1.5707963267948965  ; approximately

procedure: flsqrt fl

Returns the principal square root of z. For a negative argument, the result may be a NaN, or may be some meaningless flonum.

(flsqrt +inf.0)               ==>  +inf.0

procedure: flexpt fl1 fl2

Returns fl1 raised to the power fl2. For nonzero fl1

fl1fl2 = ez2 log z1

0fl is 1 if z = 0, and 0 if fl is positive. Otherwise, the result may be a NaN, or may be some meaningless flonum.

Fixnum/Flonum Conversions

procedure: fixnum->flonum fx

Returns a flonum that is numerically closest to its argument.

Note: The result of this procedure may not be numerically equal to its argument, because the fixnum precision may be greater than the flonum precision.

If the argument to fixnum->flonum is not a fixnum, then an error is signalled, unless the implementation is running in unsafe mode, in which case all bets are off.

Exact Arithmetic

The exact arithmetic provides generic operations on exact numbers; these operations correspond to their mathematical counterparts. The exact numbers include rationals of arbitrary precision, and exact rectangular complex numbers. A rational number with a denominator of 1 is indistinguishable from its numerator. An exact rectangular complex number with a zero imaginary part is indistinguishable from its real part.

procedure: exact-number? ex
procedure: exact-complex? ex
procedure: exact-rational? ex
procedure: exact-integer? ex

These numerical type predicates can be applied to any kind of argument, including non-numbers. They return #t if the object is an exact number of the named type, and otherwise return #f. In general, if a type predicate is true of a number then all higher type predicates are also true of that number. Consequently, if a type predicate is false of a number, then all lower type predicates are also false of that number.

We will use ex, ex1, ex2, and ex3 as metavariables that range over the exact complex numbers, ef, ef1, ef2, and ef3 as metavariables that range over the exact rational numbers, and ei, ei1, ei2, and ei3 as metavariables that range over the exact integers.

If an argument to the following procedures that corresponds to an exact (rational, integer) metavariable is not actually an exact (rational, integer) number, then these procedures signal an error (and should signal an error even in unsafe mode).

procedure: exact= ex1 ex2 ex3 ...
procedure: exact>? ef1 ef2 ef3 ...
procedure: exact<? ef1 ef2 ef3 ...
procedure: exact>=? ef1 ef2 ef3 ...
procedure: exact<=? ef1 ef2 ef3 ...

These procedures return #t if their arguments are (respectively): equal, monotonically increasing, monotonically decreasing, monotonically nondecreasing, or monotonically nonincreasing #f otherwise.

procedure: exact-zero? ex
procedure: exact-positive? ef
procedure: exact-negative? ef
procedure: exact-odd? ei
procedure: exact-even? ei

These numerical predicates test an exact number for a particular property, returning #t or #f. Exact-zero? tests if the number is exact=? to zero, exact-positive? tests if it is greater than zero, exact-negative? tests if it is less than zero, exact-odd? tests if it is odd, exact-even? tests if it is even.

procedure: exact-max ef1 ef2 ...
procedure: exact-min ef1 ef2 ...

These procedures return the maximum or minimum of their arguments.

procedure: exact+ ex1 ex2 ...
procedure: exact* ex1 ex2 ...

These procedures return the sum or product of their arguments.

procedure: exact- ex1 ex2 ...
procedure: exact- ex
procedure: exact/ ex1 ex2 ...
procedure: exact/ ex

With two or more arguments, these procedures return the difference or quotient of their arguments, associating to the left. With one argument, however, they return the additive or multiplicative inverse of their argument. Exact/ signals an error if a divisor is 0.

procedure: exact-abs ef

This procedure returns the absolute value of its argument.

procedure: exact-div+mod ef1 ef2
procedure: exact-div ef1 ef2
procedure: exact-mod ef1 ef2
procedure: exact-div0+mod0 ef1 ef2
procedure: exact-div0 ef1 ef2
procedure: exact-mod0 ef1 ef2

These procedures implement number-theoretic integer division and return the results of the corresponding mathematical operations specified in the section on Integer Division. In each case, x1 must be neither infinite nor a NaN, and x2 must be nonzero; otherwise, an exception is raised.

(exact-div x1 x2)         ==> x1 div x2
(exact-mod x1 x2)         ==> x1 mod x2
(exact-div+mod x1 x2)     ==> x1 div x2, x1 mod x2 ; two return values
(exact-div0 x1 x2)        ==> x1 div0 x2
(exact-mod0 x1 x2)        ==> x1 mod0 x2
(exact-div0+mod0 x1 x2)   ==> x1 div0 x2, x1 mod0 x2 ; two return values

procedure: exact-gcd ei1 ei2 ...
procedure: exact-lcm ei1 ei2 ...

These procedures return the greatest common divisor or least common multiple of their arguments.

procedure: exact-numerator ef
procedure: exact-denominator ef

These procedures return the numerator or denominator of their argument. The result is computed as if the argument was represented as a fraction in lowest terms. The denominator is always positive. The denominator of 0 is defined to be 1.

procedure: exact-floor ef
procedure: exact-ceiling ef
procedure: exact-truncate ef
procedure: exact-round ef

These procedures return exact integers. Exact-floor returns the largest integer not larger than ef. Exact-ceiling returns the smallest integer not smaller than ef. Exact-truncate returns the integer closest to ef whose absolute value is not larger than the absolute value of ef. Exact-round returns the closest integer to ef, rounding to even when ef is halfway between two integers.

procedure: exact-expt ef1 ei2

Returns ef1 raised to the power ei2. 0ei is 1 if ei = 0 and 0 if ei is positive. Otherwise, this procedure signals an error.

procedure: exact-make-rectangular ex1 ex2
procedure: exact-real-part ex
procedure: exact-imag-part ex

The arguments of exact-make-rectangular must be exact rationals. Suppose z is a complex number such that z = ef1 + ef2*i. Then:

(exact-make-rectangular ef1 ef2) ==> z
(exact-real-part z)              ==> ef1
(exact-imag-part z)              ==> ef2

procedure: exact-integer-sqrt ei

Ei must be a non-negative exact integer; if it is not, an error is signalled. Exact-integer-sqrt returns two non-negative exact integers s and r where ei = s2 + r and ei < (s+1)2.

procedure: exact-not ei

Returns the exact integer whose two's complement representation is the one's complement of the two's complement representation of its argument.

procedure: exact-and ei1 ...
procedure: exact-ior ei1 ...
procedure: exact-xor ei1 ...

These procedures return the exact integer that is the bit-wise "and", "inclusive or", or "exclusive or" of the two's complement representations of their arguments. If they are passed only one argument, they return that argument. If they are passed no arguments, they return the integer (either -1 or 0) that acts as identity for the operation.

procedure: exact-if ei1 ei2 ei3

Returns the exact integer that is the result of the following computation:

    (exact-ior (exact-and ei1 ei2)
               (exact-and (exact-not ei1) ei3))

procedure: exact-bit-count ei

If the argument is non-negative, this procedure returns the number of 1 bits in the two's complement representation of its argument. Otherwise it returns the number of 0 bits in the two's complement representation of its argument.

procedure: exact-length ei

These procedures return the exact integer that is the result of the following computation:

    (do ((result 0 (+ result 1))
         (bits (if (exact-negative? ei)
                   (exact- ei)
                   ei)
               (exact-arithmetic-shift bits -1)))
        ((exact-zero? bits)
         result))

procedure: exact-first-bit-set ei

This procedures returns the index of the least significant 1 bit in the two's complement representation of its argument. If the argument is 0, then -1 is returned.

(exact-first-bit-set 0)        ==>  -1
(exact-first-bit-set 1)        ==>  0
(exact-first-bit-set -4)       ==>  2

procedure: exact-bit-set? ei1 ei2

If the second argument is negative, then an error is signalled. Otherwise returns the result of the following computation:

    (not (exact-zero?
          (exact-and (exact-arithmetic-shift-left 1 ei2)
                     ei1)))

procedure: exact-copy-bit ei1 ei2 ei3

If the second argument is negative, or the third argument is anything other than 0 or 1, then an error is signalled. Otherwise returns the result of the following computation:

    (let* ((mask (exact-arithmetic-shift-left 1 ei2)))
      (exact-if mask
                (exact-arithmetic-shift-left ei3 ei2)
                ei1))

procedure: exact-bit-field ei1 ei2 ei3

If the second or third argument is negative, or the second argument is greater than the third, then an error is signalled. Otherwise returns the result of the following computation:

    (let* ((mask (exact-not
                  (exact-arithmetic-shift-left -1 ei3))))
      (exact-arithmetic-shift-right (exact-and ei1 mask)
                                    ei2))

procedure: exact-copy-bit-field ei1 ei2 ei3 ei4

If the second or third argument is negative, or the second argument is greater than the third, then an error is signalled. Otherwise returns the result of the following computation:

    (let* ((to    fx1)
           (start fx2)
           (end   fx3)
           (from  fx4)
           (mask1 (exact-logical-shift-left -1 start))
           (mask2 (exact-not
                   (exact-arithmetic-shift-left -1 end)))
           (mask (fixnum-and mask1 mask2)))
      (exact-if mask from to))

procedure: exact-arithmetic-shift ei1 ei2

Returns the exact integer result of the following computation:

    (exact-floor (* ei1 (expt 2 ei2)))

procedure: exact-arithmetic-shift-left ei1 ei2
procedure: exact-arithmetic-shift-right ei1 ei2

If the second argument is non-negative, then exact-arithmetic-shift-left returns the same result as exact-arithmetic-shift, and (exact-arithmetic-shift-right fx1 fx2) returns the same result as (exact-arithmetic-shift fx1 (exact- fx2)). If the second argument is negative, then an error is signalled.

procedure: exact-rotate-bit-field ei1 ei2 ei3 ei4

If the second, third, and fourth arguments are non-negative, or the fourth argument is greater than or equal to the difference between the second and third arguments, then an error is signalled.

    (let* ((n     ei1)
           (start ei2)
           (end   ei3)
           (count ei4)
           (width (exact- end start)))
      (if (exact-positive? width)
          (let* ((count (exact-mod count width))
                 (field0 (exact-bit-field n start end))
                 (field1 (exact-arithmetic-shift field0 count))
                 (field2 (exact-arithmetic-shift field0
                                                 (exact- width count)))
                 (field (exact-ior field1 field2)))
            (exact-copy-bit-field n field start end))
          n))

procedure: exact-reverse-bit-field ei1 ei2 ei3

If the second or third argument is negative, or the second argument is greater than the third, then an error is signalled. Otherwise returns the result obtained from the first argument by reversing the bit field specified by the second and third arguments.

(exact-reverse-bit-field #b1010010 1 4)    ==>  88 ; #b1011000
(exact-reverse-bit-field #b1010010 91 -4)  ==>  error

Inexact Arithmetic

The inexact arithmetic provides generic operations on inexact numbers. The inexact numbers include inexact reals and inexact complex numbers, both of which are distinguishable from the exact numbers. The inexact complex numbers include the flonums, and the procedures described here behave consistently with the corresponding flonum procedures if passed flonum arguments.

procedure: inexact-number? obj
procedure: inexact-complex? obj
procedure: inexact-real? obj
procedure: inexact-rational? obj
procedure: inexact-integer? obj

These numerical type predicates can be applied to any kind of argument, including non-numbers. They return #t if the object is an inexact number of the named type, and otherwise they return #f. In general, if a type predicate is true of a number then all higher type predicates are also true of that number. Consequently, if a type predicate is false of a number, then all lower type predicates are also false of that number.

We will use in, in1, in2, and in3 as metavariables that range over the inexact numbers, ir, ir1, ir2, and ir3 as metavariables that range over the inexact real numbers, if, if1, if2, and if3 as metavariables that range over the inexact rationals. and ii, ii1, ii2, and ii3 as metavariables that range over the inexact integers.

If an argument to the following procedures that corresponds to an inexact metavariable is not actually an inexact number, then these procedures signal an error (and should signal an error even in unsafe mode). The same holds true for arguments corresponding to inexact real metavariables.

procedure: inexact=? in1 in2 in3 ...
procedure: inexact>? ir1 ir2 ir3 ...
procedure: inexact<? ir1 ir2 ir3 ...
procedure: inexact>=? ir1 ir2 ir3 ...
procedure: inexact<=? ir1 ir2 ir3 ...

These procedures return #t if their arguments are (respectively): equal, monotonically increasing, monotonically decreasing, monotonically nondecreasing, or monotonically nonincreasing #f otherwise. These predicates are required to be transitive.

procedure: inexact-zero? in
procedure: inexact-positive? ir
procedure: inexact-negative? ir
procedure: inexact-odd? ii
procedure: inexact-even? ii
procedure: inexact-finite? in
procedure: inexact-infinite? in
procedure: inexact-nan? in

These numerical predicates test an inexact number for a particular property, returning #t or #f. Inexact-zero? tests if the number is inexact=? to zero, inexact-positive? tests if it is greater than zero, inexact-negative? tests if it is less than zero, inexact-odd? tests if it is odd, inexact-even? tests if it is even, inexact-finite? tests if it is not an infinity and not a NaN, inexact-infinite? tests if it is an infinity, inexact-nan? tests if it is a NaN.

procedure: inexact-max ir1 ir2 ...
procedure: inexact-min ir1 ir2 ...

These procedures return the maximum or minimum of their arguments.

procedure: inexact+ in1 in2 ...
procedure: inexact* in1 in2 ...

These procedures return the sum or product of their arguments.

procedure: inexact- in1 in2 ...
procedure: inexact- in
procedure: inexact/ in1 in2 ...
procedure: inexact/ in

With two or more arguments, these procedures return the difference or quotient of their arguments, associating to the left. With one argument, however, they return the additive or multiplicative inverse of their argument.

procedure: inexact-abs in

This procedure returns the absolute value of its argument.

procedure: inexact-div+mod ir1 ir2
procedure: inexact-div ir1 ir2
procedure: inexact-mod ir1 ir2
procedure: inexact-div0+mod0 ir1 ir2
procedure: inexact-div0 ir1 ir2
procedure: inexact-mod0 ir1 ir2

These procedures implement number-theoretic integer division and return the results of the corresponding mathematical operations specified in the section on Integer Division. In each case, x1 must be neither infinite nor a NaN, and x2 must be nonzero; otherwise, an exception is raised.

(inexact-div x1 x2)         ==> x1 div x2
(inexact-mod x1 x2)         ==> x1 mod x2
(inexact-div+mod x1 x2)     ==> x1 div x2, x1 mod x2 ; two return values
(inexact-div0 x1 x2)        ==> x1 div0 x2
(inexact-mod0 x1 x2)        ==> x1 mod0 x2
(inexact-div0+mod0 x1 x2)   ==> x1 div0 x2, x1 mod0 x2 ; two return values

procedure: inexact-gcd ii1 ii2 ...
procedure: inexact-lcm ii1 ii2 ...

These procedures return the greatest common divisor or least common multiple of their arguments.

procedure: inexact-numerator if
procedure: inexact-denominator if

These procedures return the numerator or denominator of their argument. The result is computed as if the argument was represented as a fraction in lowest terms. The denominator is always positive. The denominator of 0 is defined to be 1.

procedure: inexact-floor ir
procedure: inexact-ceiling ir
procedure: inexact-truncate ir
procedure: inexact-round ir

These procedures return inexact integers for real arguments that are not infinities or NaNs. For such arguments, inexact-floor returns the largest integer not larger than ir. Inexact-ceiling returns the smallest integer not smaller than ir. Inexact-truncate returns the integer closest to in whose absolute value is not larger than the absolute value of in. Inexact-round returns the closest integer to in, rounding to even when in is halfway between two integers.

Rationale: Round rounds to even for consistency with the default rounding mode specified by the IEEE floating point standard.

Although infinities and NaNs are not integers, these procedures return an infinity when given an infinity as an argument, and a NaN when given a NaN.

procedure: inexact-exp in
procedure: inexact-log in
procedure: inexact-sin in
procedure: inexact-cos in
procedure: inexact-tan in
procedure: inexact-asin in
procedure: inexact-atan in
procedure: inexact-atan ir1 ir2

These procedures compute the usual transcendental functions. Inexact-log computes the natural logarithm of in (not the base ten logarithm). Inexact-asin, Inexact-acos, and Inexact-atan compute arcsine (sin-1), arccosine (cos-1), and arctangent (tan-1), respectively. The two-argument variant of Inexact-atan computes `(inexact-angle (inexact-make-rectangular ir1 ir2))' (see below).

In general, the mathematical functions log, arcsine, arccosine, and arctangent are multiply defined. The value of log z is defined to be the one whose imaginary part lies in the range from -pi (inclusive if -0.0 is distinguished, exclusive otherwise) to pi (inclusive). With log defined this way, the values of sin-1 z, cos-1 z, and tan-1 z are according to the following formulae:

sin-1 z = -i log (i z + sqrt(1 - z2))

cos-1 z = pi / 2 - sin-1 z

tan-1 z = (log (1 + i z) - log (1 - i z)) / (2 i)

If the function has a real-valued limit as its argument tends toward positive infinity, then that is the value returned by the function applied to +inf.0. If the function has a real-valued limit as its argument tends toward negative infinity, then that is the value returned by the function applied to -inf.0.

In the event that these formulae do not yield a real result for the given arguments, the result may be +nan.0, or may be some meaningless inexact number.

Specifically, the range of `(inexact-atan x y)' is as in the following table. The asterisk (*) indicates that the entry applies to implementations that distinguish minus zero.

y Condition x Condition Range of result
y = 0.0 x > 0.0 0.0
* y = +0.0 x > 0.0 +0.0
* y = -0.0 x > 0.0 -0.0
y > 0.0 x > 0.0 0.0 < result< pi/2
y > 0.0 x = 0.0 pi/2
y > 0.0 x < 0.0 pi/2 < result< pi
y = 0.0 x < 0 pi
* y = +0.0 x < 0.0 +pi
* y = -0.0 x < 0.0 -pi
y < 0.0 x < 0.0 -pi< result< -pi/2
y < 0.0 x = 0.0 -pi/2
y < 0.0 x > 0.0 -pi/2 < result< 0.0
y = 0.0 x = 0.0 undefined
* y = +0.0 x = +0.0 +0.0
* y = -0.0 x = +0.0 -0.0
* y = +0.0 x = -0.0 +pi
* y = -0.0 x = -0.0 -pi

The above specification follows the branch cut specification of Common Lisp in [Pitman 1996], and [Steele 1990], which in turn cites [Penfield 1981]; refer to these sources for more detailed discussion of branch cuts, boundary conditions, and implementation of these functions.

procedure: inexact-sqrt in

Returns the principal square root of z. For a negative argument, the result may be +nan.0, or may be some meaningless inexact number. With log defined as above, the value of `(inexact-sqrt in)' could be expressed as

e(log in) / 2

procedure: inexact-expt in1 in2

Returns in1 raised to the power in2. For nonzero in1

in1in2 = ein2 log in1

0.0z is 1 if z = 0.0, and 0.0 if `(inexact-real-part z)' is positive. Otherwise, this procedure reports a violation of an implementation restriction or returns an unspecified number.

procedure: inexact-make-rectangular ir1 ir2
procedure: inexact-make-polar ir1 ir2
procedure: inexact-real-part in
procedure: inexact-imag-part in
procedure: inexact-magnitude in
procedure: inexact-angle in

Suppose in1, in2, in3, and in4 are inexact rational numbers, and z is a complex number, such that z = in1 + in2*i = in3 * ei*in4. Then (inexactly):

(inexact-make-rectangular in1 in2) ==> z
(inexact-make-rectangular in3 in4) ==> z
(inexact-real-part z)              ==> in1
(inexact-imag-part z)              ==> in2
(inexact-magnitude z)              ==> |in3|
(inexact-angle z)                  ==> inangle

where -pi <= inangle <= pi with inangle = in4 + 2pi n for some integer n.

(inexact-angle -1.0)         ==> pi
(inexact-angle -1.0+0.0)     ==> pi
(inexact-angle -1.0-0.0)     ==> -pi ; if -0.0 is distinguished

Moreover, suppose in1, in2 are such that either in1 or in2 is an infinity, then

(inexact-make-rectangular in1 in2) ==> z
(inexact-magnitude z)              ==> +inf.0

Generic Arithmetic

The section on exactness reads as follows:

Scheme numbers are either exact or inexact. A number is exact if it was written as an exact constant or was derived from exact numbers using only exact operations. A number is inexact if it is infinite, if it was written as an inexact constant, if it was derived using inexact ingredients, or if it was derived using inexact operations. Thus inexactness is a contagious property of a number. A number is inexact if it was written as an inexact constant or was derived from inexact numbers. Thus inexactness is contagious. The generic operations generally return the correct exact result when all of their arguments are exact and the result is mathematically well-defined, but return an inexact result when any argument is inexact. Exceptions are sqrt, exp, log, sin, cos, tan, asin, acos, atan, expt, make-polar, magnitude, and angle, which are allowed (but not required) to return inexact results even when given exact arguments, as indicated in the specification of these procedures.

One general exception to the rule above is that an implementation may return an exact result despite inexact arguments if that exact result would be the correct result for all possible substitutions of exact arguments for the inexact ones.

Each exact number corresponds to a single mathematical number. It is the programmer's responsibility to avoid using exact numbers with magnitude or precision too large to be represented in the implementation. For inexact numbers, it is the programmer's responsibility to avoid using complex numbers with magnitude too large to be represented in the implementation.

It is the programmer's responsibility to avoid using numbers with magnitude or significand too large to be represented in the implementation.

If two implementations produce exact results for a computation that did not involve inexact intermediate results, the two ultimate results will be mathematically equivalent. This is generally not true of computations involving inexact numbers because approximate methods such as floating point arithmetic may be used, but it is the duty of each implementation to make the result as close as practical to the mathematically ideal result.

Rational operations such as + should always produce exact results when given exact arguments. If the operation is unable to produce an exact result, then it may either report the violation of an implementation restriction or it may silently coerce its result to an inexact value. See section 6.2.3.

With the exception of inexact->exact exact-round, exact-ceiling, exact-floor, and exact-truncate, the operations described in this section must return inexact results when given any inexact arguments.

The procedures described here behave consistently with the corresponding inexact- procedure if passed inexact arguments, and with the corresponding exact- procedure if passed exact arguments.

We will use z, z1, z2, and z3 as metavariables that range over the complex numbers, x, x1, x2, and x3 as metavariables that range over the real numbers, q, q1, q2, and q3 as metavariables that range over the rationals. and n, n1, n2, and n3 as metavariables that range over the inexact integers.

If an argument to the following procedures that corresponds to a metavariable is not in the set specified for that metavariable, then these procedures signal an error (and should signal an error even in unsafe mode).

procedure: = z1 z2 z3 ...
procedure: < x1 x2 x3 ...
procedure: > x1 x2 x3 ...
procedure: <= x1 x2 x3 ...
procedure: >= x1 x2 x3 ...

These procedures return #t if their arguments are (respectively): equal, monotonically increasing, monotonically decreasing, monotonically nondecreasing, or monotonically nonincreasing #f otherwise.

(= +inf.0 +inf.0)           ==>  #t
(= -inf.0 +inf.0)           ==>  #f
(= -inf.0 -inf.0)           ==>  #t
For any real number x that is neither infinite nor NaN:
(< -inf.0 x +inf.0))        ==>  #t
(> +inf.0 x -inf.0))        ==>  #t

These predicates are required to be transitive.

Note: The traditional implementations of these predicates in Lisp-like languages are not transitive.

Note: While it is not an error to compare inexact numbers using these predicates, the results may be unreliable because a small inaccuracy may affect the result; this is especially true of = and zero?. When in doubt, consult a numerical analyst.

procedure: zero? z
procedure: positive? x
procedure: negative? x
procedure: odd? n
procedure: even? n
procedure: finite? x
procedure: infinite? x
procedure: nan? x

These numerical predicates test a number for a particular property, returning #t or #f. See note above. Zero? tests if the number is = to zero, positive? tests if it is greater than zero, negative? tests if it is less than zero, odd? tests if it is odd, even? tests if it is even, finite? tests if it is not an infinity and not a NaN, infinite? tests if it is an infinity, nan? tests if it is a NaN.

(positive? +inf.0)            ==>  #t
(negative? -inf.0)            ==>  #t
(finite? +inf.0)              ==>  #f
(finite? 5)                   ==>  #t
(finite? 5.0)                 ==>  #t
(infinite? 5.0)               ==>  #f
(infinite? +inf.0)            ==>  #t

procedure: max x1 x2 ...
procedure: min x1 x2 ...

These procedures return the maximum or minimum of their arguments.

(max 3 4)                              ==>  4    ; exact
(max 3.9 4)                            ==>  4.0  ; inexact
For any real number x:
(max +inf.0 x)                         ==>  +inf.0
(min -inf.0 x)                         ==>  -inf.0

Note: If any argument is inexact, then the result will also be inexact (unless the procedure can prove that the inaccuracy is not large enough to affect the result, which is possible only in unusual implementations). If min or max is used to compare numbers of mixed exactness, and the numerical value of the result cannot be represented as an inexact number without loss of accuracy, then the procedure may report a violation of an implementation restriction.

procedure: + z1 ...
procedure: * z1 ...

These procedures return the sum or product of their arguments.

(+ 3 4)                                ==>  7
(+ 3)                                  ==>  3
(+)                                    ==>  0
(+ +inf.0 +inf.0)                      ==>  +inf.0
(+ +inf.0 -inf.0)                      ==>  +nan.0

(* 4)                                  ==>  4
(*)                                    ==>  1
(* 5 +inf.0)                           ==>  +inf.0
(* -5 +inf.0)                          ==>  -inf.0
(* +inf.0 +inf.0)                      ==>  +inf.0
(* +inf.0 -inf.0)                      ==>  -inf.0
(* 0 +inf.0)                           ==>  0 or +nan.0
(* 0 +nan.0)                           ==>  0 or +nan.0
For any real number x that is neither infinite nor NaN:
(+ +inf.0 x)                           ==>  +inf.0
(+ -inf.0 x)                           ==>  -inf.0
(+ +nan.0 x)                           ==>  +nan.0
For any real number x that is neither infinite nor NaN nor 0:
(* +nan.0 x)                           ==>  +nan.0

If any of these procedures are applied to mixed non-rational real and non-real complex arguments, they either report a violation of an implementation restriction or return an unspecified number.

procedure: - z
optional procedure: - z1 z2 ...
procedure: / z
optional procedure: / z1 z2 ...

With two or more arguments, these procedures return the difference or quotient of their arguments, associating to the left. With one argument, however, they return the additive or multiplicative inverse of their argument.

(- 3 4)                                ==>  -1
(- 3 4 5)                              ==>  -6
(- 3)                                  ==>  -3
(- +inf.0 +inf.0)                      ==>  +nan.0

(/ 3 4 5)                              ==>  3/20
(/ 3)                                  ==>  1/3
(/ 0.0)                                ==>  +inf.0
(/ 1.0 0)                              ==>  +inf.0 or error
(/ -1 0.0)                             ==>  -inf.0
(/ +inf.0)                             ==>  0.0
(/ 0 0.0)                              ==>  +nan.0 0 or +nan.0
(/ 0.0 0)                              ==>  +nan.0 or error
(/ 0.0 0.0)                            ==>  +nan.0

If any of these procedures are applied to mixed non-rational real and non-real complex arguments, they either report a violation of an implementation restriction or return an unspecified number.

procedure: abs x

Abs returns the absolute value of its argument.

(abs -7)                               ==>  7
(abs -inf.0)                           ==>  +inf.0

procedure: div+mod x1 x2
procedure: div x1 x2
procedure: mod x1 x2
procedure: div0+mod0 x1 x2
procedure: div0 x1 x2
procedure: mod0 x1 x2

These procedures implement number-theoretic integer division and return the results of the corresponding mathematical operations specified in the section on Integer Division. In each case, x1 must be neither infinite nor a NaN, and x2 must be nonzero; otherwise, an exception is raised.

(div x1 x2)         ==> x1 div x2
(mod x1 x2)         ==> x1 mod x2
(div+mod x1 x2)     ==> x1 div x2, x1 mod x2 ; two return values
(div0 x1 x2)        ==> x1 div0 x2
(mod0 x1 x2)        ==> x1 mod0 x2
(div0+mod0 x1 x2)   ==> x1 div0 x2, x1 mod0 x2 ; two return values

procedure: gcd r1 n1 ...
procedure: lcm r1 n1 ...

Note: This is the R5RS definition.

These procedures return the greatest common divisor or least common multiple of their arguments. The result is always non-negative.

For exact integer arguments, these procedures are the familiar number theoretic operators:

(gcd 32 -36)                           ==>  4
(gcd)                                  ==>  0
(lcm 32 -36)                           ==>  288
(lcm 32.0 -36)                         ==>  288.0 ; inexact
(lcm)                                  ==>  1

procedure: numerator q
procedure: denominator q

These procedures return the numerator or denominator of their argument; the result is computed as if the argument was represented as a fraction in lowest terms. The denominator is always positive. The denominator of 0 is defined to be 1.

(numerator (/ 6 4))                    ==>  3
(denominator (/ 6 4))                  ==>  2
(denominator
  (exact->inexact (/ 6 4)))            ==>  2.0

procedure: floor x
procedure: ceiling x
procedure: truncate x
procedure: round x

These procedures accept finite real numbers and return integers. These procedures return inexact integers on inexact arguments that are not infinities or NaNs, and exact integers on exact rational arguments. For such arguments, floor returns the largest integer not larger than x. Ceiling returns the smallest integer not smaller than x. Truncate returns the integer closest to x whose absolute value is not larger than the absolute value of x. Round returns the closest integer to x, rounding to even when x is halfway between two integers.

Rationale: Round rounds to even for consistency with the default rounding mode specified by the IEEE floating point standard.

Note: If the argument to one of these procedures is inexact, then the result will also be inexact. If an exact value is needed, the result should be passed to the inexact->exact procedure.

Although infinities and NaNs are not integers, these procedures return an infinity when given an infinity as an argument, and a NaN when given a NaN.

(floor -4.3)                           ==>  -5.0
(ceiling -4.3)                         ==>  -4.0
(truncate -4.3)                        ==>  -4.0
(round -4.3)                           ==>  -4.0

(floor 3.5)                            ==>  3.0
(ceiling 3.5)                          ==>  4.0
(truncate 3.5)                         ==>  3.0
(round 3.5)                            ==>  4.0  ; inexact

(round 7/2)                            ==>  4    ; exact
(round 7)                              ==>  7

(floor +inf.0)                         ==>  +inf.0
(ceiling -inf.0)                       ==>  -inf.0
(round +nan.0)                         ==>  +nan.0

procedure: rationalize x y

Rationalize returns the simplest rational number differing from x by no more than y. A rational number r1 is simpler than another rational number r2 if r1 = p1/q1 and r2 = p2/q2 (in lowest terms) and |p1|<= |p2| and |q1| <= |q2|. Thus 3/5 is simpler than 4/7. Although not all rationals are comparable in this ordering (consider 2/7 and 3/5) any interval contains a rational number that is simpler than every other rational number in that interval (the simpler 2/5 lies between 2/7 and 3/5). Note that 0 = 0/1 is the simplest rational of all.

(rationalize
  (inexact->exact .3) 1/10)            ==> 1/3    ; exact
(rationalize .3 1/10)                  ==> #i1/3  ; inexact

(rationalize +inf.0 3)                 ==>  +inf.0
(rationalize +inf.0 +inf.0)            ==>  +nan.0
(rationalize 3 +inf.0)                 ==>  0.0

procedure: exp z
procedure: log z
procedure: sin z
procedure: cos z
procedure: tan z
procedure: asin z
procedure: acos z
procedure: atan z
procedure: atan y x

These procedures compute the usual transcendental functions. Exp computes the base-e exponential of z. Log computes the natural logarithm of z (not the base ten logarithm). Asin, acos, and atan compute arcsine (sin-1), arccosine (cos-1), and arctangent (tan-1), respectively. The two-argument variant of `atan' computes (angle (make-rectangular x y)) (see below), even in implementations that don't support general complex numbers.

In general, the mathematical functions log, arcsine, arccosine, and arctangent are multiply defined. The value of log z is defined to be the one whose imaginary part lies in the range from -pi (exclusive) (inclusive if -0.0 is distinguished, exclusive otherwise) to pi (inclusive).

The value of log z for non-real z is defined in terms of log on real numbers as

log z = log |z| + xanglei

where xangle is the angle of z as specified below.

With log defined this way, the values of sin-1 z, cos-1 z, and tan-1 z are according to the following formulae:

sin-1 z = -i log (i z + sqrt(1 - z2))

cos-1 z = pi / 2 - sin-1 z

tan-1 z = (log (1 + i z) - log (1 - i z)) / (2 i)

If the function has a real-valued limit as its argument tends toward positive infinity, then that is the value returned by the function applied to +inf.0. If the function has a real-valued limit as its argument tends toward negative infinity, then that is the value returned by the function applied to -inf.0.

These procedures may return inexact results even when given exact arguments.

In the event that these formulae do not yield a real result for the given arguments, the result may be +nan.0, or may be some meaningless inexact number.

Specifically, the range of `(atan x y)' is as in the following table. The asterisk (*) indicates that the entry applies to implementations that distinguish minus zero.

y Condition x Condition Range of result
y = 0.0 x > 0.0 0.0
* y = +0.0 x > 0.0 +0.0
* y = -0.0 x > 0.0 -0.0
y > 0.0 x > 0.0 0.0 < result< pi/2
y > 0.0 x = 0.0 pi/2
y > 0.0 x < 0.0 pi/2 < result< pi
y = 0.0 x < 0 pi
* y = +0.0 x < 0.0 +pi
* y = -0.0 x < 0.0 -pi
y < 0.0 x < 0.0 -pi< result< -pi/2
y < 0.0 x = 0.0 -pi/2
y < 0.0 x > 0.0 -pi/2 < result< 0.0
y = 0.0 x = 0.0 undefined
* y = +0.0 x = +0.0 +0.0
* y = -0.0 x = +0.0 -0.0
* y = +0.0 x = -0.0 +pi
* y = -0.0 x = -0.0 -pi
* y = +0.0 x = 0 pi/2
* y = -0.0 x = 0 -pi/2

The above specification follows the branch cut specification of Common Lisp in [Pitman 1996], and [Steele 1990], which in turn cites [Penfield 1981]; refer to these sources for more detailed discussion of branch cuts, boundary conditions, and implementation of these functions.

(exp +inf.0)                   ==> +inf.0
(exp -inf.0)                   ==> 0.0
(log +inf.0)                   ==> +inf.0
(log 0.0)                      ==> -inf.0
(log 0)                        is an error
(log -inf.0)                   ==> +nan.0 +inf.0+pii
(atan -inf.0)                  ==> -1.5707963267948965 ; approximately
(atan +inf.0)                  ==> 1.5707963267948965  ; approximately
(log -1.0+0.0i)                ==> 0.0+pii
(log -1.0-0.0i)                ==> 0.0-pii ; if -0.0 is distinguished

The functions sin, cos, tan, asin, and acos either return +nan.0 or report a violation of an implementation restriction when given +inf.0, -inf.0, or +nan.0 as an argument.

procedure: sqrt z

Returns the principal square root of z. For real rational z, the result will have either positive real part, or zero real part and non-negative imaginary part. With log defined as above, the value of `(sqrt in)' could be expressed as

e(log in) / 2

Sqrt may return an inexact result even when given an exact argument.

(sqrt -5)                   ==>  0.0+2.23606797749979i
(sqrt +inf.0)               ==>  +inf.0
(sqrt -inf.0)               ==>  +nan.0 +inf.0i

procedure: expt z1 z2

Returns z1 raised to the power z2. For nonzero z1

z1z2 = ez2 log z1

0z is 1 if z = 0, and 0 if `(real-part z)' is positive. Otherwise, this procedure reports a violation of an implementation restriction or returns an unspecified number.

For an exact z1 and an exact integer z2, `(expt z1 z2)' must return an exact result. For all other values of z1 and z2, `(expt z1 z2)' may return an inexact result, even when both z1 and z2 are exact.

0^0 is 1.

For inexact arguments not both zero

(define (expt z1 z2) (exp (* (if (zero? z1) (real-part z2) z2) (log z1))))
(expt 0.0 z)
returns 1.0 for z equal to 0;
returns 0.0 for z having positive real part (including +/0.);
returns +/0. for z having negative real part (including -/0.); and
returns 0/0. or reports a violation of an implementation restriction otherwise.
(expt 5 3)                  ==>  125
(expt 5 -3)                 ==>  1/125
(expt 5 0)                  ==>  1
(expt 0 5)                  ==>  0
(expt 0 5+.0000312i)        ==>  0
(expt 0 -5)                 ==>  +inf.0 unspecified
(expt 0 -5+.0000312i)       ==>  +inf.0 unspecified
(expt 0 0)                  ==>  +nan.0 1
(expt 0.0 0.0)              ==>  +nan.0 1.0

procedure: make-rectangular x1 x2
procedure: make-polar x3 x4
procedure: real-part z
procedure: imag-part z
procedure: magnitude z
procedure: angle z

These procedures are part of every implementation that supports general complex numbers. Suppose x1, x2, x3, and x4 are real numbers and z is a complex number such that

z = x1 + i x2 = x3 ei x4

Then

(make-rectangular x1 x2)               ==> z
(make-polar x3 x4)                     ==> z
(real-part z)                          ==> x1
(imag-part z)                          ==> x2
(magnitude z)                          ==> |x3|
(angle z)                              ==> xangle

where -pi <<= xangle <= pi with xangle = in4 + 2pi n for some integer n.

Moreover, suppose x1, x2 are such that either x1 or x2 is an infinity, then

(make-rectangular x1 x2)      ==> z
(magnitude z)                 ==> +inf.0

Make-polar, magnitude, and angle may return inexact results even when given exact arguments.

(angle -1)                    ==> pi
(angle +inf.0)                ==> 0.0
(angle -inf.0)                ==> pi
(angle -1.0+0.0)              ==> pi
(angle -1.0-0.0)              ==> -pi ; if -0.0 is distinguished

Rationale: Magnitude is the same as abs for a real argument, but abs must be present in all implementations, whereas magnitude need only be present in implementations that support general complex numbers.

Design Rationale

Background and Rationale concerning two issues of R6RS arithmetic (written by William D Clinger, 29 July 2005)

(Most experts who read this note will want to start with the section Problems With Flow Analysis, thereby skipping the history and background information that is intended to make this note more self-contained and accessible.)

Lucier's Proposal

In 1998, for the Scheme workshop before ICFP '98 in Baltimore, Brad Lucier proposed several changes that were intended to bring Scheme's inexact arithmetic into line with the IEEE floating point standards and with other recommended practice for transcendental functions and complex arithmetic. Most of Lucier's proposals would have applied only to implementations that use IEEE floating point for inexact arithmetic, and would thus act as recommendations, much like the appendices on inexact arithmetic that were published with the IEEE standard for Scheme. A few of Lucier's proposals would have required changes to the Scheme standards themselves, however.

Few of the workshop attendees knew enough about the IEEE floating point standards to discuss Lucier's proposal. A straw poll was taken, and came out 31-0 in favor of bringing Scheme into line with IEEE floating point and with current practice, trusting experts to work out the details.

Here are the highlights of Lucier's proposal, taken verbatim from my notes on the 1998 workshop:

The behavior of eqv? on inexact numbers would change. If x and y are inexact reals represented as IEEE floating point numbers, then `(eqv? x y)' would be true if and only if x and y are equal and have the same base, sign, number of bits in the exponent, number of bits in the significand, and the same biased exponents and significands. For example, `(eqv? +0. -0.) would be false, as would `(eqv? 1e8 1d8)' in an implementation for which 1d8 has more precision than 1e8. In most implementations `(eqv? x y)' would be computed by a bit-level comparison of the floating point representations for x and y.

`(real? 4.3+0.i)' and `(real? 4.3-0.i)' would be false, although `(real? 4.3+0i)' and `(real? 4.3-0i)' would remain true (assuming an implementation allows the real and imaginary parts of a complex number to have a different exactness, which is not required by the Scheme standards and would not be required by Lucier's proposal).

Truncate, round, ceiling, and floor would be defined only on rationals, not on all reals. The motivation for this is that infinities and NaNs would be reals but not rationals, and there is no meaningful integer value that these procedures could return for infinities and NaNs. Similarly the first argument to rationalize would be required to be a rational.

The branch cuts for certain transcendental functions would change to conform to current practice.

Kahan reportedly would like for `(max 1 +nan.0 2)' to return 2 instead of +nan.0, but this would conflict with the guiding principle of Scheme's inexact arithmetic so I oppose this. Returning an inexact 2.0 would be consistent with Scheme's arithmetic, and would not require any changes to the Scheme standards.

The Issues

The purpose of this note is to explain why it is desirable for Scheme's reals to have an exact zero as their imaginary part.

This note also discusses whether the proposed "flonum" type should be a synonym for "inexact real", or merely a subset of the inexact reals.

Numerical Types

Scheme's numerical types are the exactness types { exact, inexact }, the tower types { integer, rational, real, complex, number }, and the Cartesian product of the exactness types with the tower types, where < t1, t2 > is regarded as a subtype of both t1 and t2.

These types have an aesthetic symmetry to them, but they are not equally important. Judging by the number of R5RS procedures whose domain is restricted to values of some numerical type, the most important numerical types are the exact integers, the integers, the rationals, the reals, and the complex numbers.

Many programmers and implementors regard R5RS's focus on those particular numerical types as something of a mistake. In practice, there is reason to believe that the most important numerical types are the exact integers, the exact rationals, the inexact reals, and the inexact complex numbers. The following section explores one of the reasons those four types are so important in practice.

Closure Properties

Scheme's types are not completely arbitrary. Each type corresponds to a set of values that turns up repeatedly as the natural domain or range of the functions that are computed by Scheme's standard procedures. The reason these types turn up so often is that they are closed under certain sets of operations.

The exact integers, for example, are closed under the integral operations of addition, subtraction, and multiplication. The exact rationals are closed under the rational operations, which consist of the integral operations plus division (although we must make a special case for division by zero). The reals (and inexact reals) are closed under some (often inexact) interpretation of rational and irrational operations such as exp and sin, but are not closed under operations such as log, sqrt, and expt. The complex (and inexact complex) numbers are closed under the largest set of operations.

Representation Types

The Scheme standards give implementations considerable freedom with respect to the representation of expressed values, but the usual approach is to represent the numerical types as unions of the representation types that appear on the right hand sides of the following domain equations:

exact integer = fixnum + bignum
exact rational = fixnum + bignum + ratnum
inexact real = flonum
inexact complex = flonum + compnum
integer = fixnum + bignum + subset of flonum
rational = fixnum + bignum + ratnum + subset of flonum
real = fixnum + bignum + ratnum + flonum
complex = rectangular + compnum

These domain equations are typical of most implementations, but many variations are possible. The flonum representation type, for example, is often divided into several distinct precisions.

Representation-specific Operations

A naive implementation of Scheme's arithmetic operations is slow compared to the arithmetic operations of most other languages, mainly because most operations must perform some sort of case dispatch on the representation types of their arguments. The potential for this case dispatch arises when the type of an operation's argument is represented by a union of two or more representation types, or because the operation is required to signal an error when given an argument of an incorrect type. (The second reason can be regarded as a special case of the first.)

To make Scheme's arithmetic more efficient, many implementations provide sets of operations whose domain is restricted to a single representation type, and which are not expected to signal an error when given arguments of incorrect type.

Alternatively, or in addition, several compilers perform some kind of flow analysis that attempts to infer the representation types of expressions. When a single representation type can be inferred for each argument of an operation, and those types match the types expected by some representation-specific version of the operation, then the compiler can substitute the specific version for the more general version that was specified in the source code.

Flow Analysis

Flow analysis is performed by solving the type and interval constraints that arise from such things as:

The purpose of flow analysis (as motivated in this note) is to infer a single representation type for each argument of an operation. That places a premium on predicates and closure properties from which a single representation type can be inferred.

Problems With Flow Analysis

In practice, the most important single representation types are fixnum, flonum, and compnum. These are the representation types for which a short sequence of machine code can be generated when the representation type is known, but for which considerably less efficient code will probably have to be generated when the representation type cannot be inferred.

The fixnum representation type is not closed under any R5RS operations, so it is hard for flow analysis to infer the fixnum type from portable R5RS code. Sometimes the combination of a more general type (e.g. exact integer) and an interval (e.g. [0,n), where n is known to be a fixnum) can imply the fixnum representation type. Adding fixnum-specific operations that map fixnums to fixnums (by computing modulo 2^n, say) greatly increases the number of fixnum representation types that a compiler can infer.

The flonum representation type is not closed under operations such as sqrt and expt, so flow analysis tends to break down in the presence of those operations. This is unfortunate, because those operations are normally used only with arguments for which the result is expected to be a flonum. Adding flonum-specific versions such as flsqrt and flexpt improves the effectiveness of flow analysis.

The R5RS creates a more insidious problem by defining `(real? z)' to be true if and only if `(zero? (imag-part z))' is true. This means, for example, that -2.5+0.0i is real. If -2.5+0.0i is represented as a compnum, then the compiler cannot rely on x being a flonum during the <real_case> of `(if (real? x) <real_case> <unreal_case>)'. This problem could be fixed by writing all of the arithmetic operations so that any compnum with a zero imaginary part is converted to a flonum before it is returned, but that merely creates an analogous problem for compnum arithmetic, as explained below. A better way to fix the problem is as Lucier recommended: change the definition of Scheme's real numbers by defining `(real? z)' to be true if and only if `(imag-part z)' is an exact zero.

The compnum representation type is closed under virtually all operations, provided no operation that accepts two compnums as its argument ever returns a flonum. To work around the problem described in the paragraph above, several implementations automatically convert compnums with a zero imaginary part to the flonum representation. This practice virtually destroys the effectiveness of flow analysis for inferring the compnum representation, so it is not a good workaround. To improve the effectiveness of flow analysis, it is better to change the definition of Scheme's real numbers as described in the paragraph above.

Recommendations

To improve the effectiveness of flow analysis and to improve the efficiency of arithmetic, I recommend that the R6RS:

Flonums

When flonum-specific operations are added to Scheme, should they take any inexact real as arguments? Or should their arguments be restricted to some subtype of the inexact reals?

Another way to describe this issue is to specify that the flonum-specific operations take flonums as arguments and return flonums as results. Then the question becomes: Are the flonums coextensive with the inexact reals? Or are the flonums allowed to be a proper subset of the inexact reals?

I do not think this question is terribly important. Most current implementations have only one representation for inexact reals, and having only one representation for the inexact reals improves the effectiveness of flow analysis, so implementations that try to provide efficient arithmetic on inexact reals are likely to make "flonum" synonymous with "inexact real" even if the R6RS allows otherwise.

One reason for allowing "flonum" to be a proper subtype of "inexact real" is to give implementations freedom to experiment with unusual representation strategies. Another possibility is that an implementation might take "flonum" to mean the representation that is supported most efficiently by the hardware, e.g. IEEE double precision, while providing other precisions that might be more space-efficient for certain applications.

On the other hand, defining "flonum" to be a synonym for "inexact real" would simplify the presentation of flonum arithmetic, and would eliminate a couple of procedures that would otherwise have to be provided and somehow specified (e.g. real->flonum and flonum?).

I recommend that the R6RS define "flonum" as a synonym for "inexact real", but this is not a strong recommendation. It doesn't matter very much.

Reference Implementation

The reference implementation contains a reference implementation for most of the procedures described here, written in mostly R5RS Scheme, and based purely on integer and flonum arithmetic of the underlying Scheme implementation. It builds the entire numeric tower on fixnums and flonums. The code is set up to run under Scheme 48, but should be easy to adapt to other Scheme systems. It will be completed upon withdrawal of this SRFI.

References

Acknowledgements

We thank Anton van Straaten, Kent Dybvig, Sebastian Egner, Bradley Lucier, Mike Ashley, Marc Feeley, Matthew Flatt, Manuel Serrano for direct and indirect assistance producing this draft. Special thanks to Aubrey Jaffer for his work on SRFI 70. We also thank David Van Horn, who did a very thorough job editing the original draft. All remaining errors (of which there probably are many) are our own.

Copyright

Copyright (C) William D Clinger and Michael Sperber (2005, 2006). All Rights Reserved.

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.


Editor: David Van Horn