Numerical types

Mathematically, numbers are arranged into a tower of subtypes in which each level is a subset of the level before it: number; complex number; real number; rational number; integer.

For example, 3 is an integer. Therefore 3 is also a rational, a real, and a complex number. The same is true of the Scheme numbers that model 3. For Scheme numbers, these types are defined by the predicates number?, complex?, real?, rational?, and integer?.

There is no simple relationship between a number’s type and its representation inside a computer. Although most implementations of Scheme will offer at least two different representations of 3, these different representations denote the same integer.

Scheme’s numerical operations treat numbers as abstract data, as independent of their representation as possible. Although an implementation of Scheme may use multiple internal representations of numbers, this ought not to be apparent to a casual programmer writing simple programs.

Type: number

The type of Scheme numbers.

Type: quantity

The type of quantities optionally with units. This is a sub-type of number.

Type: complex

The type of complex numbers. This is a sub-type of quantity.

Type: real

The type of real numbers. This is a sub-type of complex.

Type: rational

The type of exact rational numbers. This is a sub-type of real.

Type: integer

The type of exact Scheme integers. This is a sub-type of rational.

Kawa allows working with expressions of “primitive” types, which are supported by the JVM without object allocation, and using builtin arithmetic. Using these types may be much faster, assuming the compiler is able to infer that the variable or expression has primitive type.

Type: long

Type: int

Type: short

Type: byte

These are fixed-sized primitive signed exact integer types, of respectively 64, 32, 18, and 8 bits. If a value of one of these types needs to be converted to an object, the standard classes java.lang.Long, java.lang.Integer, java.lang.Short, or java.lang.Byte, respectively, are used.

Type: ulong

Type: uint

Type: ushort

Type: ubyte

These are fixed-sized primitive unsigned exact integer types, of respectively 64, 32, 18, and 8 bits. These are presented at runtime using the corresponding signed types (long, int, short, or byte). However, for arithmetic the Kawa compiler generates code to perform the “mathematically correct” result, truncated to an unsigned result rather than signed. If a value of one of these types needs to be converted to an object, the classes gnu.math.ULong, gnu.math.UInt, gnu.math.UShort, or gnu.math.UByte is used.

Type: double

Type: float

These are fixed-size primitive inexact floating-point real types, using the standard 64-bit or 32-bit IEEE representation. If a value of one of these types needs to be converted to an object, the standard classes java.lang.Double, or java.lang.Float is used.

Exactness

It is useful to distinguish between numbers that are represented exactly and those that might not be. For example, indexes into data structures must be known exactly, as must some polynomial coefficients in a symbolic algebra system. On the other hand, the results of measurements are inherently inexact, and irrational numbers may be approximated by rational and therefore inexact approximations. In order to catch uses of inexact numbers where exact numbers are required, Scheme explicitly distinguishes exact from inexact numbers. This distinction is orthogonal to the dimension of type.

A Scheme 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. Thus inexactness is a contagious property of a number. In particular, an exact complex number has an exact real part and an exact imaginary part; all other complex numbers are inexact complex numbers.

If two implementations produce exact results for a computation that did not involve inexact intermediate results, the two ultimate results will be mathematically equal. This is generally not true of computations involving inexact numbers since approximate methods such as floating-point arithmetic may be used, but it is the duty of the 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.

Except for exact, the operations described in this section must generally return inexact results when given any inexact arguments. An operation may, however, return an exact result if it can prove that the value of the result is unaffected by the inexactness of its arguments. For example, multiplication of any number by an exact zero may produce an exact zero result, even if the other argument is inexact.

Specifically, the expression (* 0 +inf.0) may return 0, or +nan.0, or report that inexact numbers are not supported, or report that non-rational real numbers are not supported, or fail silently or noisily in other implementation-specific ways.

The procedures listed below will always return exact integer results provided all their arguments are exact integers and the mathematically expected results are representable as exact integers within the implementation: -, *, +, abs, ceiling, denominator, exact-integer-sqrt, expt, floor, floor/, floor-quotient, floor-remainder, gcd, lcm, max, min, modulo, numerator, quotient, rationalize, remainder, square, truncate, truncate/, truncate-quotient, truncate-remainder.

Numerical promotion and conversion

When combining two values of different numeric types, the values are converted to the first line in the following that subsumes (follows) both types. The computation is done using values of that type, and so is the result. For example adding a long and a float converts the former to the latter, yielding a float.

Note that short, byte, ushort, ubyte are converted to int regardless, even in the case of a single-operand operation, such as unary negation. Another exception is trancendental functions (such as cos), where integer operands are converted to double.

  • int subsumes short, byte, ushort, ubyte.

  • uint

  • long

  • ulong

  • java.lang.BigInteger

  • integer (i.e. gnu.math.IntNum)

  • rational (i.e. gnu.math.RatNum)

  • float

  • double

  • gnu.math.FloNum

  • real (i.e. gnu.math.RealNum)

  • number

  • complex

  • quantity

When comparing a primitive signed integer value with a primitive unsigned integer (for example < applied to a int and a ulong) the mathemically correct result is computed, as it converting both operands to integer.