SRFI-94: Type-Restricted Numerical Functions

Abstract

In the coding of numerial calculations in latent-typed languages it is good practice to assure that those calculations are using the intended number system. The most common number systems for programmatic calculations are the integers, reals, and complexes. This SRFI introduces 14 real-only and 3 integer-only variants of R5RS procedures to facilitate numerical type checking and declaration.

For more information see: SRFI-94: Type-Restricted Numerical Functions

Issues

With the exception of = and zero?, Scheme's numerical comparison operators are already restricted to real numbers.

The larger question is whether to make integer-only versions of =, zero?, <, >, <=, >=, positive?, and negative?. (odd? and even? are already integer-only).

Because quotient, modulo, and remainder constrain both their inputs and outputs, and because vector-ref, vector-set!, string-ref, string-set!, list-ref, list-tail, odd?, and even? constrain one of their inputs as integers, I think that integer-only comparison operators are not needed.

Terminology

Use of the terms integer, real, and complex follows R5RS and SRFI-70. All integers are real; all reals are complex.

Rationale

The infinities introduced by SRFI-70 extend the range of Scheme numerical calculations. But with that larger range, the signaling of numerical errors resulting from bad inputs or mistakes in program logic is delayed, and sometimes missed entirely.

The SRFI-77 shotgun approach creates type-restricted variants of all the numerical operations, but does not allow those operations to signal errors when they are forced out of bounds! For instance flsqrt and inexact-sqrt:

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

SRFI-77 here misses an opportunity to increase the declarative power of Scheme numerical operations. By the present SRFI making real-sqrt and real-ln of negative numbers an error, compilers can deduce that the expressions calculating the inputs to real-sqrt and real-ln must return non-negative reals. Similarly, use of the other real-only transcendental functions declares their inputs and outputs to be real.

For addition, subtraction, negation, and multiplication, the results of the operation will be members of the same number system as the operation's inputs when those inputs are all of the same number system. It would be unnecessary tedium to apply run-time tests to intermediate or final results of chains of these common operations.

Division of reals yields a real; division of complexes yields a complex. So real and complex division also need not be distinguished. max of integers yields an integer; and max of reals yields a real. So these are not distinguished; neither is min.

But division of integers can yield a non-integer. To address this, R5RS defines a division operator, quotient, which is specified only for integer arguments. This SRFI mandates that quotient, remainder, and modulo signal an error when passed an argument which is not an exact-integer.

The criterion is exact-integer instead of just integer so that the results of integer calculations are guaranteed to be of the correct type to pass as index arguments.

Arithmetic operations on mixtures of integer, real, and complex numbers are well defined and very common in use. This SRFI does not alter R5RS behavior of arithmetic operations with mixed type inputs.

This SRFI introduces some integer-only and real-only variants of the transcendental functions of R5RS. Those type-restricted functions are mandated to signal an error if called with arguments or producing results which are not in the designated number system. Thus (real-sqrt -1) must signal an error even though -1 is an exact real. Following SRFI-70, #+inf.0 and #-inf.0 are real; NaNs are not. None of these infinities is an integer.

The complex transcendental functions from R5RS are unchanged by this SRFI. They accept complex (hence also integer and real) arguments. The functions abs, make-rectangular, and make-polar; and atan when called with two arguments are changed to require them to signal an error when passed a non-real number. Note that integers are real numbers.

Although the language of this SRFI calls for error signaling, that signaling is not limited to run-time. An error can be reported during compilation. Run-time type testing can be skipped if the compiler can prove that arguments are of the correct type and within the correct range. It would be reasonable for SRFI-77's "unsafe mode" to disable run-time checking.

+----------------------------------+
|         single argument          |
|----------------------------------|
|Exact-integer |  Real   | Complex |
|--------------+---------+---------|
|              |real-exp |exp      |
|--------------+---------+---------|
|              |real-ln  |ln       |
|--------------+---------+---------|
|              |real-atan|atan     |
|--------------+---------+---------|
|              |real-acos|acos     |
|--------------+---------+---------|
|              |real-asin|asin     |
|--------------+---------+---------|
|              |real-tan |tan      |
|--------------+---------+---------|
|              |real-cos |cos      |
|--------------+---------+---------|
|              |real-sin |sin      |
|--------------+---------+---------|
|abs           |abs      |magnitude|
|--------------+---------+---------|
|integer-sqrt  |real-sqrt|sqrt     |
+----------------------------------+
+--------------------------------+
|         multi-argument         |
|--------------------------------|
|Exact-integer |  Real   |Complex|
|--------------+---------+-------|
|integer-expt  |real-expt|expt   |
|--------------+---------+-------|
|integer-log   |real-log |       |
|--------------+---------+-------|
|              |atan     |       |
|--------------+---------+-------|
|+             |+        |+      |
|--------------+---------+-------|
|-             |-        |-      |
|--------------+---------+-------|
|*             |*        |*      |
|--------------+---------+-------|
|              |/        |/      |
|--------------+---------+-------|
|quotient      |quo      |       |
|--------------+---------+-------|
|modulo        |mod      |       |
|--------------+---------+-------|
|remainder     |rem      |       |
|--------------+---------+-------|
|max           |max      |       |
|--------------+---------+-------|
|min           |min      |       |
+--------------------------------+

The arithmetic-shift and integer-length procedures of SRFI-60 are related to the base-2 exponential and logarithm respectively, but are not included in the table.

mod and rem are the real functions from Common-Lisp. quo is the analogous division (truncate (/ x1 x2)).

Although not a type-restricted function, ln is added as a synonym for log because log is not used consistently to denote the natural logarithm.

Real-log returns the logarithm of its second argument using its first argument as the base. integer-log is the analogous two-argument logarithm function for integers.

Specification

[procedure] real-exp x
[procedure] real-ln x
[procedure] real-log y x
[procedure] real-sin x
[procedure] real-cos x
[procedure] real-tan x
[procedure] real-asin x
[procedure] real-acos x
[procedure] real-atan x
[procedure] atan y x

These procedures are part of every implementation that supports general real numbers; they compute the usual transcendental functions. Real-ln computes the natural logarithm of x (not the base ten logarithm); real-log computes the logarithm of x base y, which is (/ (real-ln x) (real-ln y)) If arguments x and y are not both real; or if the correct result would not be real, then these procedures signal an error.

[procedure] real-sqrt x

For non-negative real x the result will be its positive square root; otherwise an error will be signaled.

[procedure] integer-sqrt n

For non-negative integer n returns the largest integer whose square is less than or equal to n; otherwise signals an error.

[procedure] integer-log k1 k2

Returns the largest exact integer whose power of k1 is less than or equal to k2. If k1 or k2 is not a positive exact integer, then integer-log signals an error.

[procedure] integer-expt n1 n2

Returns n1 raised to the power n2 if that result is an exact integer; otherwise signals an error.

(integer-expt 0 n2)

returns 1 for n2 equal to 0;

returns 0 for positive integer n2;

signals an error otherwise.

[procedure] real-expt x1 x2

Returns x1 raised to the power x2 if that result is a real number; otherwise signals an error.

(real-expt 0.0 x2)

returns 1.0 for x2 equal to 0.0;

returns 0.0 for positive real x2;

signals an error otherwise.

[procedure] quo x1 x2
[procedure] rem x1 x2
[procedure] mod x1 x2

x2 should be non-zero.

(quo x1 x2)                     ==> n_q
(rem x1 x2)                     ==> x_r
(mod x1 x2)                     ==> x_m

where n_q is x1/x2 rounded towards zero, 0 < |x_r| < |x2|, 0 < |x_m| < |x2|, x_r and x_m differ from x1 by a multiple of x2, x_r has the same sign as x1, and x_m has the same sign as x2.

From this we can conclude that for x2 not equal to 0,

(= x1 (+ (* x2 (quo x1 x2))
      (rem x1 x2)))
                                  ==>  #t

provided all numbers involved in that computation are exact.

(quo 2/3 1/5)                          ==>  3
(mod 2/3 1/5)                  ==>  1/15

(quo .666 1/5)                 ==>  3.0
(mod .666 1/5)                 ==>  65.99999999999995e-3
[procedure] ln z

These procedures are part of every implementation that supports general real numbers. Ln computes the natural logarithm of z.

In general, the mathematical function ln is multiply defined. The value of ln z is defined to be the one whose imaginary part lies in the range from -pi (exclusive) to pi (inclusive).

Note

The specification of two-argument atan above and the following six procedures are changed from R5RS.

Additions and changes are marked in red.

[procedure] make-rectangular x1 x2
[procedure] make-polar x3 x4

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 e^i x4

Then

(make-rectangular x1 x2)               ==> z
(make-polar x3 x4)                     ==> z

where -pi < x_angle <= pi with x_angle = x4 + 2pi n for some integer n.

If an argument is not real, then these procedures signal an error.

[procedure] abs x

For real argument x, abs returns the absolute value of x; otherwise it signals an error.

(abs -7)                               ==>  7
[procedure] quotient n1 n2
[procedure] remainder n1 n2
[procedure] modulo n1 n2

These procedures implement number-theoretic (integer) division. If n1 is not an exact integer, or if n2 is not an exact non-zero integer, an error is signaled. All three procedures return exact integers. If n1/n2 is an integer:

(quotient n1 n2)                   ==> n1/n2
(remainder n1 n2)                  ==> 0
(modulo n1 n2)                     ==> 0

If n1/n2 is not an integer:

(quotient n1 n2)                   ==> n_q
(remainder n1 n2)                  ==> x_r
(modulo n1 n2)                     ==> x_m

where n_q is n1/n2 rounded towards zero, 0 < |x_r| < |n2|, 0 < |x_m| < |n2|, x_r and x_m differ from n1 by a multiple of n2, x_r has the same sign as n1, and x_m 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)))
                                  ==>  #t
(modulo 13 4)                          ==>  1
(remainder 13 4)                       ==>  1

(modulo -13 4)                         ==>  3
(remainder -13 4)                      ==>  -1

(modulo 13 -4)                         ==>  -3
(remainder 13 -4)                      ==>  1

(modulo -13 -4)                        ==>  -1
(remainder -13 -4)                     ==>  -1

Author

Copyright (C) Aubrey Jaffer 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.

Version history