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.
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 the underlying machine's representation; flonum arithmetic is typically limited-precision floating-point arithmetic using the underlying machine's representation.)
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. This SRFI describes revisions that would satisfy both of us and states the choice between both of them as an issue to be resolved in the discussion period of this SRFI. 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:
eqv?
to
behave more sensibly with inexact numbers
Members of the Scheme community are encouraged to express themselves on the following issues:
read
, write
, and several
other procedures must know about the external representations
of all numbers.
fx
operators
that signal an error upon overflow and wfx
operators that wrap.
real->flonum
.
(See the Design Rationale.)
+
)
remain in R6RS? Here are five possible answers, phrased in
terms of the +
procedure:
+
is not defined in R6RS.
+
is defined to be a synonym for the ex+
,
so its domain is restricted to exact arguments, and always
returns an exact result.
+
is defined as the union of the ex+
and in+
procedures, so all of its arguments are
required to have the same exactness, and the exactness of its
result is the same as the exactness of its arguments.
+
is defined as in R5RS, but with the increased
portability provided by requiring the full numeric tower.
This alternative is described in the section
R5RS-style Generic Arithmetic.
+
is defined to return an exact result in all
cases, even if one or more of its arguments is inexact.
This alternative is described in the section
Generic Exact Arithmetic.
quotient+remainder
, gcd
and lcm
), and others that are easily abused
(such as fxabs
). Should these be pruned?
Given that this SRFI suggests requiring all implementations to support the general complex numbers, shouldRationale:
Magnitude
is the same asabs
for a real argument, butabs
must be present in all implementations, whereasmagnitude
need only be present in implementations that support general complex numbers.
abs
(and
exabs
and inabs
) be removed?
real?
, rational?
, and integer?
predicates must
return false for complex numbers with an imaginary part
of inexact zero, as non-realness is now contagious.
This causes possibly unexpected behavior: `(zero?
0+0.0i)' returns true despite `(integer?
0+0.0i)' returning false.
Possibly, new predicates realistic?
,
rationalistic?
, and integral?
should be
added to say that a number can be coerced to the specified type
(and back) without loss.
(See the Design Rationale.)
x|53
default for the mantissa width discriminates
against implementations that default to unusually good representations,
such as IEEE extended precision. Are there any such implementations?
Do we expect such implementations in the near future?
(floor +inf.0)
' return +inf.0
or
signal an error instead? (Similarly for ceiling
,
flfloor
, infloor
, etc.)
ex
prefixes? Or should they be extended to work
on inexact integers as well?
Most implementations of Scheme fall into one of the following categories:
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:
+nan.0
as a real number whose value is so
indeterminate that it might represent any real number within
the closed interval [-inf.0
,+inf.0
].
Moreover, unlike SRFI 70, this SRFI doesn't extend
quotient
, remainder
, and modulo
to real numbers, but instead provides additional operations
div
and mod
, 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. Moreover, the section Generic Exact Arithmetic is also new here. 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.
This SRFI describes several alternative approaches to completely generic arithmetic. The alternative described in the section R5RS-style Generic Arithmetic follows the essence of R5RS, with the advantage of historical continuity and contagiousness of inexact operations. The alternative described in section Generic Exact Arithmetic has exactness be contagious, with the advantage of greater transparency and the validity of standard algebraic laws over the R5RS-style generic arithmetic. The section describing generic exact arithmetic opens with a more detailed rationale. Other alternatives build on the exact arithmetic described in section Exact Arithmetic and the inexact arithmetic described in section Inexact Arithmetic.
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.
This SRFI adds the following external representations to Scheme:
+inf.0
represents the result of
(/ 1.0 0.0)
-inf.0
represents the result of
(/ -1.0 0.0)
+nan.0
represents the result of
(/ 0.0 0.0)
, and may represent other NaNs as well.
(This SRFI does not require read/write invariance for NaNs.)
|
p
is an external representation that denotes the
best binary floating point approximation to x
using a p-bit significand.
For example, 1.1|53
is an external representation for the
best approximation to 1.1 in IEEE double precision.
x|
53
.
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 signficand.
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.
This SRFI allows a Scheme implementation to run code in one of two global modes: "safe" and "unsafe" mode. These affect the fixnum and the 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 must provide no such checking. This distinction allows an implementation to generate efficient numerical code at the cost of avoiding run-time checking. The R6RS should require a Scheme implementation to provide the safe mode.
This SRFI uses the phrase "all bets are off" to describe that the behavior of a procedure is unspecified for certain arguments in unsafe mode. Specifically, a procedure call for which all bets are off is free to crash the system.
The R6RS specification of eqv? for numbers should be changed as follows.
The eqv?
procedure returns #t
if:
=
, section see section 6.2
Numbers).=
, section see section 6.2 Numbers), and
yield the same results (in the sense of eqv?
) when passed
as arguments to any other procedure that can be defined
as a finite composition of Scheme's standard arithmetic
procedures.
The eqv?
procedure returns #f
if:
=
procedure
returns #f
. eqv?
) when passed as arguments to any other procedure
that can be defined as a finite composition of Scheme's
standard arithmetic procedures.
number?
obj
complex?
obj
real?
obj
rational?
obj
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.
(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.
(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 (real? -inf.0) ==> #t (real? +nan.0) ==> #t (rational? -inf.0) ==> #f (rational? +nan.0) ==> #f (rational? 6/10) ==> #t (rational? 6/3) ==> #t (integer? 3+0i) ==> #t (integer? 3.0) ==> #t (integer? 8/4) ==> #t (number? +nan.0) ==> #t (complex? +nan.0) ==> #t (complex? +inf.0) ==> #t (real? +nan.0) ==> #t (rational? +nan.0) ==> #f (integer? -inf.0) ==> #f
Note: The behavior of these type predicates on inexact numbers is unreliable, because any inaccuracy may affect the result.
Note: In many implementations therational?
procedure will be the same asreal?
, and thecomplex?
procedure will be the same asnumber?
, 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.
exact?
z
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
exact->inexact
z
inexact->exact
z
Exact->inexact
returns an inexact representation of z.
The value returned is the
inexact number that is numerically closest 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.
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.
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.
number->string
z
number->string
z radix
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 p ≥ precision 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.
string->number
string
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 ofstring->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, thenstring->number
is permitted to return #f whenever string uses the polar or rectangular notations for complex numbers. If all numbers are integers, thenstring->number
may return #f whenever the fractional notation is used. If all numbers are exact, thenstring->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, thenstring->number
may return #f whenever a decimal point is used. An implementation may return#f
for"+nan.0"
.
The div
, mod
,
div+mod
, and quotient+remainder
procedures are new and not in SRFI 70. The definitions of
quotient
and remainder
are as in
R5RS, not as in SRFI 70.
For various kinds of arithmetic (fixnum, flonum, exact, inexact, and generic), this SRFI provides operations for performing integer division. Their variants differ mainly in the domains they operate on, not in substance. The following specification is of the general variant. Later sections will link back to this one where appropriate.
div+mod
x1 x2
div
x1 x2
mod
x1 x2
div
returns an integer; mod
returns a real:
div+mod
returns two values, an integer and a real.
(div x1 x2) ==> nd (mod x1 x2) ==> xm (div+mod x1 x2) ==> (div x1 x2) (mod x1 x2) ; two return valueswhere:
(div x1 0) ==> 0
Rationale: These operations generalize integer division to real numbers. Moreover, even on integers they are better suited than
quotient
andremainder
to implement modular reduction. For details, see the paper by Egner et. al [Egner et al. 2004].
quotient+remainder
n1 n2
quotient
n1 n2
remainder
n1 n2
modulo
n1 n2
These procedures implement number-theoretic (integer)
division. n2 should be non-zero.
Quotient
, remainder
, and modulo
return integers; quotient+remainder
returns two integers.
(quotient+remainder n1 n2) ==> (quotient n1 n2) (remainder n1 n2) ; two return values
If n1/n2 is an integer:
(quotient n1 n2) ==> n1/n2 (remainder n1 n2) ==> 0 (modulo n1 n2) ==> 0If n1/n2 is not an integer:
(quotient n1 n2) ==> nq (remainder n1 n2) ==> nr (modulo n1 n2) ==> nm
where nq is n1/n2 rounded towards zero, 0 < |nr| < |n2|, 0 < |nm| < |n2|, nr and nm differ from n1 by an integral multiple of n2, nr has the same sign as n1, and nm has the same sign as n2.
From this we can conclude that for integers n1 and n2 with n2 not equal to 0,
(= n1 (+ (* n2 (quotient n1 n2)) (remainder n1 n2))) ==> #tprovided all numbers involved in that computation are exact.
(quotient+remainder 13 4) ==> 3 1 (modulo 13 4) ==> 1 (remainder 13 4) ==> 1 (quotient+remainder -13 4) ==> -3 -1 (modulo -13 4) ==> 3 (remainder -13 4) ==> -1 (quotient+remainder -13 4) ==> -3 1 (modulo 13 -4) ==> -3 (remainder 13 -4) ==> 1 (modulo -13 -4) ==> -1 (remainder -13 -4) ==> -1
Quotient
, remainder
and modulo
can easily be defined through div
and mod
:
(define (sign n) (cond ((positive? n) 1) ((negative? n) -1) (else 0))) (define (quotient n1 n2) (* (sign n1) (sign n2) (div (abs n1) (abs n2)))) (define (remainder n1 n2) (* (sign n1) (mod (abs n1) (abs n2)))) (define (modulo n1 n2) (* (sign n2) (mod (* (sign n2) n1) (abs n2))))
A subset range of the exact integers is denoted 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 [lo, hi] such that lo and hi are (mathematical) integers with lo <= 0 < 1 <= hi. Every mathematical integer within an implementation's fixnum range must correspond to an exact integer that is representable within the implementation. The fixnum operations of an implementation will perform arithmetic modulo hi-lo+1.
fixnum?
obj
This returns #t if obj is a fixnum, and otherwise returns #f.
least-fixnum
greatest-fixnum
These procedures return lo and hi, the minimum and the maximum value of the fixnum range, respectively.
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.
fx=
fx1 fx2
fx>
fx1 fx2
fx<
fx1 fx2
fx>=
fx1 fx2
fx<=
fx1 fx2
These procedures return #t if their arguments are (respectively):
equal, monotonically increasing, monotonically decreasing,
monotonically nondecreasing, or monotonically nonincreasing,
#f
otherwise.
fxzero?
fx
fxpositive?
fx
fxnegative?
fx
fxodd?
fx
fxeven?
fx
These numerical predicates test a fixnum for a particular property,
returning #t or #f. Fxzero?
tests if
the number is zero, fxpositive?
tests if it is greater
than zero, fxnegative?
tests if it is less
than zero, fxodd?
tests if it is odd,
fxeven?
tests if it is even.
fxmax
fx1 fx2
fxmin
fx1 fx2
These procedures return the maximum or minimum of their arguments.
fx+
fx1 fx2
fx-
fx1 fx2
fx*
fx1 fx2
These procedures return the unique fixnum that is congruent mod hi-lo+1 to the sum, difference, or product of their arguments.
fxabs
fx
This procedure returns `(fx- 0 fx)' if fx is negative, fx otherwise.
fxdiv+mod
fx1 fx2
fxdiv
fx1 fx2
fxmod
fx1 fx2
fxquotient
fx1 fx2
fxmodulo+remainder
fx1 fx2
fxmodulo
fx1 fx2
fxremainder
fx1 fx2
These procedures implement number-theoretic integer division modulo hi-lo+1. See the section on Integer Division.
fxgcd
fx1 fx2
fxlcm
fx1 fx2
These procedures return the greatest common divisor or least common multiple of their arguments modulo hi-lo+1.
fxbitwise-not
fx
Returns the fixnum which is the one's-complement of its argument. congruent mod hi-lo+1.
fxbitwise-and
fx1 fx2
fxbitwise-ior
fx1 fx2
fxbitwise-xor
fx1 fx2
The fxbitwise-and
procedure returns the fixnum which is the bit-wise "and" of
the two's complement representations of its
arguments modulo hi-lo+1.
The fxbitwise-ior
procedure returns the fixnum which is
the bit-wise inclusive "or" of
the two's complement representations of its
arguments modulo hi-lo+1.
The fxbitwise-xor
procedure returns the fixnum which is the
bit-wise "exclusive or" of the two's complement representations of its
arguments modulo hi-lo+1.
fxarithmetic-shift
fx1 fx2
This procedure conceptually shifts the two's complement representation of fx1 fx2 bits left when fx2 > 0, and -fx2 bits right when fx2 < 0, extending the sign. (It returns fx1 when fx2 = 0.) It returns the result of that shift congruent mod hi-lo+1.
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 are closed under the flonum operations described in this section.
flonum?
obj
This returns #t if obj is a flonum, and otherwise returns #f.
We will use fl, fl1 and fl2 as metavariables that range over flonums, and ifl, ifl1 and ifl2 as metavariables that range over integral flonums, i.e. flonums that represent integers.
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.
fl=
fl1 fl2
fl<
fl1 fl2
fl<=
fl1 fl2
fl>
fl1 fl2
fl>=
fl1 fl2
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) ==> unspecified (fl< 0.0 -0.0) ==> unspecified (fl= +nan.0 fl) ==> unspecified (fl= +nan.0 fl) ==> unspecified (fl< +nan.0 fl) ==> unspecifiedThe following behavior is strongly recommended but not required by R6RS:
(fl= 0.0 -0.0) ==> #t (fl< -0.0 0.0) ==> #f (fl= +nan.0 +nan.0) ==> #f
flinteger?
fl
flzero?
fl
flpositive?
fl
flnegative?
fl
flodd?
ifl
fleven?
ifl
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,
flnan?
tests if it is a NaN.
(flnegative? -0.0) ==> #f
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.
flmax
fl1 fl2
flmin
fl1 fl2
These procedures return the maximum or minimum of their arguments.
fl+
fl1 fl2
fl-
fl1 fl2
fl*
fl1 fl2
fl/
fl1 fl2
These procedures return the flonum sum, difference, product, or quotient of their flonum arguments. In general, they should return the flonum that best approximates the mathematical sum, difference, or quotient of their arguments. (For implementations that represent flonums as IEEE binary floating point numbers, the meaning of "best" is reasonably well-defined by the IEEE standards.)
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
flabs
fl
This returns the absolute value of its argument.
fldiv+mod
fl1 fl2
fldiv
fl1 fl2
flmod
fl1 fl2
flquotient
ifl1 ifl2
flmodulo+remainder
ifl1 ifl2
flmodulo
ifl1 ifl2
flremainder
ifl1 ifl2
These procedures implement number-theoretic integer division. See the
section on Integer Division. For zero
divisors, the procedures may return +nan.0
or some
meaningless flonum.
flgcd
ifl1 ifl2
fllcm
ifl1 ifl2
These procedures return the greatest common divisor or least common multiple of their arguments.
flnumerator
fl
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 ; exampleThe following behavior is strongly recommended but not required by R6RS:
(flnumerator -0.0) ==> -0.0
flfloor
fl
flceiling
fl
fltruncate
fl
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.
(flfloor +inf.0) ==> +inf.0 (flceiling -inf.0) ==> -inf.0
flexp
fl
fllog
fl
flsin
fl
flcos
fl
fltan
fl
flasin
fl
flatan1
fl
flatan2
fl1 fl2
These procedures compute the usual transcendental functions. Fllog
computes the natural logarithm of fl (not the base ten logarithm).
Flasin
, Flacos
, and Flatan1
compute arcsine (sin-1),
arccosine (cos-1), and arctangent (tan-1), respectively.
`(flatan1 fl1 fl2)'
computes the
arc tangent of fl1/fl2. The range of
flatan1
and flatan2
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 +nan.0
, 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 `(flatan2 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 (flatan1 -inf.0) ==> -1.5707963267948965 (flatan1 +inf.0) ==> 1.5707963267948965
flsqrt
fl
Returns the principal square root of z. For a negative
argument, the result may be +nan.0
, or may be some
meaningless flonum. With flexp
and fllog
defined as above, the value of `(flsqrt fl)'
is defined as `(flexp (fl/ (fllog fl) 2.0))'
.
(flsqrt +inf.0) ==> +inf.0
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 +nan.0
, or may be some
meaningless flonum.
fixnum->flonum
fx
flonum->fixnum
fl
The fixnum->flonum
and flonum->fixnum
procedures provide for explicit conversions
between fixnums and flonums. The fixnum->flonum
procedure returns, for a flonum argument, its numerically closest
fixnum.
The flonum->fixnum
procedure returns, for a fixnum argument, its numerically closest
flonum.
Programmers should understand that these procedures
cannot be expected to return results that are
numerically equal to their arguments.
For example, suppose the fixnum range is [-8388608,8388607] and flonums are represented in IEEE double precision. Then
(fixnum->flonum 8388608) ==> 8388608.0 (flonum->fixnum 3.14159265) ==> 3 (flonum->fixnum -inf.0) ==> -8388608 (flonum->fixnum 1e20) ==> 8388607
If the argument to fixnum->flonum
is not a fixnum, or
the argument to flonum->fixnum
is NaN or not a flonum,
then an error is signalled, unless the implementation is
running in unsafe mode, in which case all bets are off.
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.
exnumber?
ex
excomplex?
ex
exrational?
ex
exinteger?
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, even in unsafe mode.
ex=
ex1 ex2 ex3 ...
ex>
ef1 ef2 ef3 ...
ex<
ef1 ef2 ef3 ...
ex>=
ef1 ef2 ef3 ...
ex<=
ef1 ef2 ef3 ...
These procedures return #t if their arguments are (respectively):
equal, monotonically increasing, monotonically decreasing,
monotonically nondecreasing, or monotonically nonincreasing
#f
otherwise.
exzero?
ex
expositive?
ef
exnegative?
ef
exodd?
ei
exeven?
ei
These numerical predicates test an exact number for a particular property,
returning #t or #f.
Exzero?
tests if
the number is ex=
to zero, expositive?
tests if it is greater
than zero, exnegative?
tests if it is less
than zero, exodd?
tests if it is odd,
exeven?
tests if it is even.
exmax
ef1 ef2 ...
exmin
ef1 ef2 ...
These procedures return the maximum or minimum of their arguments.
ex+
ex1 ex2 ...
ex*
ex1 ex2 ...
These procedures return the sum or product of their arguments.
ex-
ex1 ex2 ...
ex-
ex
ex/
ex1 ex2 ...
ex/
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. Ex/
signals an error
if a divisor is 0.
exabs
ef
This procedure returns the absolute value of its argument.
exdiv+mod
ef1 ef2
exdiv
ef1 ef2
exmod
ef1 ef2
exquotient
ei1 ei2
exmodulo+remainder
ei1 ei2
exmodulo
ei1 ei2
exremainder
ei1 ei2
These procedures implement number-theoretic integer division. See the section on Integer Division. For zero divisors, these procedures signal an error.
exgcd
ei1 ei2 ...
exlcm
ei1 ei2 ...
These procedures return the greatest common divisor or least common multiple of their arguments.
exnumerator
ef
exdenominator
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.
exfloor
ef
exceiling
ef
extruncate
ef
exround
ef
These procedures return exact integers.
Exfloor
returns the largest integer not larger than ef.
Exceiling
returns the smallest integer not smaller than ef.
Extruncate
returns the integer closest to ef whose absolute
value is not larger than the absolute value of ef. Exround
returns the
closest integer to ef, rounding to even when ef is halfway between two
integers.
exexpt
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.
exmake-rectangular
ex1 ex2
exreal-part
ex
eximag-part
ex
The arguments of exmake-rectangular
must be exact
rationals. Suppose z is a complex number such that
z = ef1 + ef2*i. Then:
(exmake-rectangular ef1 ef2) ==> z (exreal-part z) ==> ef1 (eximag-part z) ==> ef2
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.
innumber?
obj
incomplex?
obj
inreal?
obj
inrational?
obj
ininteger?
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, even in unsafe mode. The same holds true for arguments corresponding to inexact real metavariables.
in=
in1 in2 in3 ...
in>
ir1 ir2 ir3 ...
in<
ir1 ir2 ir3 ...
in>=
ir1 ir2 ir3 ...
in<=
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.
inzero?
in
inpositive?
ir
innegative?
ir
inodd?
ii
ineven?
ii
innan?
in
These numerical predicates test an inexact number for a particular property,
returning #t or #f.
Inzero?
tests if
the number is in=
to zero, inpositive?
tests if it is greater
than zero, innegative?
tests if it is less
than zero, inodd?
tests if it is odd,
ineven?
tests if it is even,
innan?
tests if it is a NaN.
inmax
ir1 ir2 ...
inmin
ir1 ir2 ...
These procedures return the maximum or minimum of their arguments.
in+
in1 in2 ...
in*
in1 in2 ...
These procedures return the sum or product of their arguments.
in-
in1 in2 ...
in-
in
in/
in1 in2 ...
in/
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.
inabs
in
This procedure returns the absolute value of its argument.
indiv+mod
ir1 ir2
indiv
ir1 ir2
inmod
ir1 ir2
inquotient
ii1 ii2
inmodulo+remainder
ii1 ii2
inmodulo
ii1 ii2
inremainder
ii1 ii2
These procedures implement number-theoretic integer division. See the section on Integer Division. For zero divisors, these procedures signal an error or may return some meaningless inexact number.
ingcd
ii1 ii2 ...
inlcm
ii1 ii2 ...
These procedures return the greatest common divisor or least common multiple of their arguments.
innumerator
if
indenominator
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.
infloor
ir
inceiling
ir
intruncate
ir
inround
ir
These procedures return inexact integers
for real arguments that are
not infinities or NaNs. For such arguments,
infloor
returns the largest integer not larger than ir.
Inceiling
returns the smallest integer not smaller than ir.
Intruncate
returns the integer closest to in whose absolute
value is not larger than the absolute value of in. Inround
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.
inexp
in
inlog
in
insin
in
incos
in
intan
in
inasin
in
inatan
in
inatan
ir1 ir2
These procedures compute the usual transcendental functions. Inlog
computes the natural logarithm of in (not the base ten logarithm).
Inasin
, Inacos
, and Inatan
compute arcsine (sin-1),
arccosine (cos-1), and arctangent (tan-1), respectively.
The two-argument variant of Inatan
computes `(inangle
(inmake-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 `(inatan 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.
insqrt
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 inlog
defined as above,
the value of `(insqrt in)'
is defined as
`(inexp (in/ (inlog in) 2.0))'
.
inexpt
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 `(inreal-part z)' is positive. Otherwise, this procedure reports a violation of an implementation restriction or returns an unspecified number.
inmake-rectangular
ir1 ir2
inmake-polar
ir1 ir2
inreal-part
in
inimag-part
in
inmagnitude
in
inangle
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):
(inmake-rectangular in1 in2) ==> z
(inmake-rectangular in3 in4) ==> z
(inreal-part z) ==> in1
(inimag-part z) ==> in2
(inmagnitude z) ==> |in3|
(inangle z) ==> inangle
where -pi <= inangle <= pi with inangle = in4 + 2pi n for some integer n.
(inangle -1.0) ==> pi (inangle -1.0+0.0) ==> pi (inangle -1.0-0.0) ==> -pi ; if -0.0 is distinguished
In this model of 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 itis infinite, if itwas written as an inexact constant, if it was derived using inexact ingredients, or if it was derived using inexact operations. The generic operations all implement inexact operations. Thus inexactness is a contagious property of a number.
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 significant too large to be represented in the implementation.
If two implementations produce exact results for a computation that did not involve inexact intermediate results or the results of numerical predicates, 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
, the operations described in this section must return inexact results when given any inexact arguments.exact-round
,exact-ceiling
,exact-floor
, andexact-truncate
The procedures described here behave consistently with the inexact arithmetic if passed inexact arguments, and consistently with the exact arithmetic 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, even in unsafe mode.
=
z1 z2 z3 ...
<
x1 x2 x3 ...
>
x1 x2 x3 ...
<=
x1 x2 x3 ...
>=
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) ==> #tFor 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
=
andzero?
. When in doubt, consult a numerical analyst.
zero?
z
positive?
x
negative?
x
odd?
n
even?
n
nan?
z
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,
nan?
tests if it is a NaN.
(positive? +inf.0) ==> #t (negative? -inf.0) ==> #t
max
x1 x2 ...
min
x1 x2 ...
These procedures return the maximum or minimum of their arguments.
(max 3 4) ==> 4 ; exact (max 3.9 4) ==> 4.0 ; inexactFor 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
ormax
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.
+
z1 ...
*
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) ==> +nan.0For 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 (* +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.
-
z1 z2
-
z
-
z1 z2 ...
/
z1 z2
/
z
/
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) ==> +nan.0 (/ 1.0 0) ==> +inf.0 (/ -1 0.0) ==> -inf.0 (/ +inf.0) ==> 0.0 (/ 0 0.0) ==> +nan.0 unspecified (/ 0.0 0) ==> +nan.0 (/ 0.0 0.0) ==> +nan.0
abs
x
Abs
returns the absolute value of its argument.
(abs -7) ==> 7 (abs -inf.0) ==> +inf.0
div+mod
x1 x2
div
x1 x2
mod
x1 x2
quotient
n1 n2
modulo+remainder
n1 n2
modulo
n1 n2
remainder
n1 n2
These procedures implement number-theoretic integer division. See the section on Integer Division.
gcd
lcm
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
numerator
q
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
floor
x
ceiling
x
truncate
x
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.
(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
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
exp
z
log
z
sin
z
cos
z
tan
z
asin
z
acos
z
atan
z
atan
y x
These procedures compute the usual transcendental functions. 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
.
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 -inf.0) ==> +nan.0 unspecified (atan -inf.0) ==> -1.5707963267948965 (atan +inf.0) ==> 1.5707963267948965 (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.00
,
-inf.0
, or +nan.0
as an argument.
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 exp
and log
defined as above, the value of `(sqrt z)'
`(exp (/ (log z) 2.0))'
.
(sqrt -5) ==> 0.0+2.23606797749979i (sqrt +inf.0) ==> +inf.0 (sqrt -inf.0) ==> +nan.0 unspecified
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.
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)
(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 (expt 0.0 0.0) ==> +nan.0 1.0
make-rectangular
x1 x2
make-polar
x3 x4
real-part
z
imag-part
z
magnitude
z
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.
(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 asabs
for a real argument, butabs
must be present in all implementations, whereasmagnitude
need only be present in implementations that support general complex numbers
bitwise-and
ei1 ei2 ...
bitwise-ior
ei1 ei2 ...
bitwise-xor
ei1 ei2 ...
The bitwise-and
procedure returns the exact integer which is the bit-wise "and" of the
two's complement representations of its
arguments.
The bitwise-ior
procedure returns the exact integer which
is the bit-wise inclusive "or" of the two's
complement representations of its
arguments.
The bitwise-xor
procedure returns the exact integer which is the
bit-wise "exclusive or" of the two's complement representations of its
arguments.
arithmetic-shift
ei1 ei2
This conceptually shifts the two's complement representation of ei1 ei2 bits left when ei2 > 0, and -ei2 bits right when ei2 < 0, extending the sign. It returns ei1 when ei2 = 0.
This specification of exact arithmetic is an alternative to the R5RS-style generic arithmetic described in the previous section.
The exact generic arithmetic provides generic operations on exact and
inexact rational and complex numbers. Each inexact number, except for
infinities and NaNs and complex numbers with infinite or NaN real or
imaginary part, is taken to represent a unique exact rational
number or exact complex number with exact rational real and imaginary
parts, namely the one produced by inexact->exact
.
With this provision, the generic exact operations correspond to their
mathematical counterparts.
Note: In this section, changes with respect SRFI 70 aren't marked specially---otherwise, the entire section would be in red.
In this model of arithmetic, the section on exactness reads as follows: Changes from the description of exactness in the previous section are highlighted in red.
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 was written as an inexact constant, if it was derived using inexact ingredients, or if it was derived using inexact operations. The generic operations all implementinexactexact operations. Consequently,inexactnessexactness is a contagious property of a number.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 using only the operations described in this section that did not involve inexact
intermediate results or the results of numerical predicatesinputs (which include the literals representing inexact numbers), 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.The transition from exactness to inexactness is always explicit throughexact->inexact
,real->flonum
(or indirectly through I/O). Consequently, inexactness cannot implicitly contaminate a computation that started out with exact numbers and does not use the (explicitly) inexact operations.
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 ofexact->inexact
the operations described in this section must return inexact results when given any inexact arguments.
Rationale: This departure from the R5RS semantics abandons the notion that the same program may compute either exactly or inexactly, depending on the input numbers. Each generic procedure always performs the same computation, which is in contrast to R5RS where the generic procedures perform different operations depending on their inputs. This means that the generic operations reliably fulfill the standard algebraic laws. For a more detailed treatment of the subject, refer to the paper by Egner et al. [Egner et al. 2004].
We will use zf, zf1, zf2, and z3 as metavariables that range over the complex numbers, excluding NaNs, infinities, or complex numbers with NaN or infinite real or imaginary part, xf, xf1, xf2, and xf3 as metavariables that range over the real numbers excluding NaNs and infinities, q, q1, q2, and q3 as metavariables that range over the rational numbers, and n, n1, n2, and n3 as metavariables that range over the integers.
If an argument to the following procedures that corresponds to one of the above metavariables is not actually a rational or complex number with rational real and imaginary part, these procedures signal an error, even in unsafe mode.
=
zf1 zf2 zf3 ...
>
xf1 xf2 xf3 ...
<
xf1 xf2 xf3 ...
>=
xf1 xf2 xf3 ...
<=
xf1 xf2 xf3 ...
These procedures return #t if their arguments are (respectively): equal, monotonically increasing, monotonically decreasing, monotonically nondecreasing, or monotonically nonincreasing.
zero?
zf
positive?
xf
negative?
xf
odd?
n
even?
n
These numerical predicates test an exact number for a particular property,
returning #t or #f.
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.
max
xf1 xf2 ...
min
xf1 xf2 ...
These procedures return the maximum or minimum of their arguments.
+
zf1 zf2 ...
*
zf1 zf2 ...
These procedures return the sum or product of their arguments.
-
zf1 zf2 ...
-
zf
/
zf1 zf2 ...
/
zf
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. /
signals an error
if a divisor is 0.
abs
xf
This procedure returns the absolute value of its argument.
div+mod
xf1 xf2
div
xf1 xf2
mod
xf1 xf2
quotient
n1 n2
modulo+remainder
n1 n2
modulo
n1 n2
remainder
n1 n2
These procedures implement number-theoretic integer division. See the section on Integer Division.
gcd
n1 n2 ...
lcm
n1 n2 ...
These procedures return the greatest common divisor or least common multiple of their arguments.
numerator
q
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.
floor
xf
ceiling
xf
truncate
xf
round
xf
These procedures return exact integers.
Floor
returns the largest integer not larger than xf.
Ceiling
returns the smallest integer not smaller than xf.
Truncate
returns the integer closest to xf whose absolute
value is not larger than the absolute value of xf. Round
returns the
closest integer to xf, rounding to even when xf is halfway between two
integers.
expt
q n
Returns q raised to the power n. 0n is 1 if n = 0 and 0 if n is positive. Otherwise, this procedure signals an error.
make-rectangular
xf1 xf2
real-part
zf
imag-part
zf
Suppose zf is a complex number such that zf = x1 + xf2*i. Then:
(make-rectangular xf1 xf2) ==> zf (real-part zf) ==> xf1 (imag-part zf) ==> xf2
bitwise-not
n
Returns the one's-complement of its argument.
bitwise-and
n1 n2 ...
bitwise-ior
n1 n2 ...
bitwise-xor
n1 n2 ...
The bitwise-and
procedure returns the exact integer which is the bit-wise "and" of the
two's complement representations of its
arguments.
The bitwise-ior
procedure returns the exact integer which
is the bit-wise inclusive
"or" of the two's
complement representations of its
arguments.
The bitwise-xor
procedure returns the exact integer which is the
bit-wise "exclusive or" of the two's complement representations of its arguments.
arithmetic-shift
n1 n2
This conceptually shifts the two's complement representation of n1 n2 bits left when n2 > 0, and -n2 bits right when n2 < 0, extending the sign. It returns n1 when n2 = 0.
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.)
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 which1d8
has more precision than1e8
. 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
, andfloor
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 torationalize
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 inexact2.0
would be consistent with Scheme's arithmetic, and would not require any changes to the Scheme standards.
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.
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.
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.
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.
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 is performed by solving the type and interval constraints that arise from such things as:
`(if (< i n) E1 E2)'
`(if (real? x) <real_case> <unreal_case>)'
`(+ <flonum> <flonum>)'
always evaluates to a flonum)
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.
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.
To improve the effectiveness of flow analysis and to improve the efficiency of arithmetic, I recommend that the R6RS:
fx+
fl+
`(imag-part 2.0)'
is an exact zero
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.
The reference implementation contains a reference implementation for 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.
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.