2.10 Numbers


2.10.1 Numeric types

ECL supports all of the Common Lisp numeric tower, which is shown in Table 2.5. The details, however, depend both on the platform on which ECL runs and on the configuration which was chosen when building ECL.

TypeDescription
fixnumSigned integer with a number of bits given by ext:fixnum-bits, fit in a machine word.
bignumArbitrary size integers, only limited by amount of memory.
ratioArbitrary size rational number, made up of two integers.
short-floatEquivalent to single-float.
single-float32-bits IEEE floating point number.
double-float64-bits IEEE floating point number.
long-floatEither equivalent to double-float, or a 96/128 bits IEEE floating point number (long double in C/C++).
rationalAn alias for (or integer ratio)
floatAn alias for (or single-float double-float short-float long-float)
realAn alias for (or rational float)
complexComplex number made of two real numbers of the above mentioned types or a <float> _Complex type in C99.

Table 2.5: Numeric types in ECL

In general, the size of a fixnum is determined by the word size of a machine, which ranges from 32 to 64 bits. Integers larger than this are implemented using the GNU Multiprecision library. Rationals are implemented using two integers, without caring whether they are fixnum or not. Floating point numbers include at least the two IEEE types of 32 and 64 bits respectively.

In machines where it is supported, it is possible to associate the lisp long-float with the machine type long double whose size ranges from 96 to 128 bits, and which are a bit slower.

In machines where a type <float> _Complex is supported numbers of type (complex float) are implemented with it, otherwise like ratios all complex numbers are pairs of numbers.


2.10.2 Floating point exceptions

ECL supports two ways of dealing with special floating point values, such as Not a Number (NaN), infinity or denormalized floats, which can occur in floating point computations. Either a condition is signaled or the value is silently used as it is. There are multiple options controlling which behaviour is selected: If ECL is built with the --without-ieee-fp configure option, then a condition is signaled for every infinity or NaN encountered. If not, floating point exceptions can be disabled at build time using the --without-fpe configure option. Otherwise, if both --with-ieee-fp and --with-fpe options are on, by default, a condition is signaled for invalid operation, division by zero and floating point overflows. This can be changed at runtime by using ext:trap-fpe. If the ECL_OPT_TRAP_SIGFPE boot option is false (see Table 1.1), no conditions are signaled by default (Note that in this case, if you enable trapping of floating point exceptions with ext:trap-fpe, then you have to install your own signal handler).

Function: ext:trap-fpe condition flag

Control the signaling of the floating point exceptions

Synopsis

condition

a symbol - one of cl:last, cl:t, cl:division-by-zero, cl:floating-point-overflow, cl:floating-point-underflow, cl:floating-point-invalid-operation, cl:floating-point-inexact or an integer.

flag

a generalized boolean

Description

If condition is last, flag is ignored and the currently enabled floating point exceptions are returned in an implementation depended format (currently an integer). Otherwise, flag determines whether the current thread will signal a floating point exception for the conditions passed in condition. condition can be either a symbol denoting a single condition, t for all conditions that are enabled by default or a value obtained from an earlier call to ext:trap-fpe with last.

See also

ECL_WITH_LISP_FPE


2.10.3 Random-States

ECL relies internally on a 32-bit Mersenne-Twister random number generator, using a relatively large buffer to precompute about 5000 pseudo-random bytes. This implies also that random states can be printed readably and also read, using the #$ macro. There is no provision to create random states from user arrays, though. Random state is printed unreadably by default.

The #$ macro can be used to initialize the generator with a random seed (an integer), an array of random bytes (mainly used for reading back printed random-state) and another random-state (syntactic sugar for copying the random-state).


2.10.4 Infinity and Not a Number

The ANSI Common-Lisp standard does not specify the behaviour of numeric functions for infinite or not number valued floating point numbers. If ECL is configured to support these special values (see the --with-ieee-fp configure option) and floating point exceptions are disabled, numeric functions generally return the same value as the corresponding C function. This means, that the output will be a NaN for a NaN input, and the “mathematically correct” value (which may be NaN, e.g. for ∞-∞) for an infinite real input. For complex floats, however, the return value of a numeric function called with a complex number for which the real or imaginary part is infinite, is undefined1.

For other functions dealing with numbers, we adopt the following behaviour:

Comparison functions

All numeric comparisons with =,<,<=,>,>= involving NaN return false. Comparing two NaNs of the same type with eql returns true.

min/max

NaN values are ignored, i.e. the maximum/minimum is taken only over the number valued parameters.

Rounding functions

All rounding functions signal an arithmetic-error if any of the given parameters are not number valued or infinite.


2.10.5 Branch cuts and signed zero

For multi-valued complex functions like asin, acos, etc. the ANSI Common-Lisp standard specifies in detail the location of the branch cuts and in particular the value of these functions on the branch cuts. ECL diverges from the standard in that the sign of zero distinguishes two sides of a branch cut. For example, the asin function includes a branch cut running from 1 to ∞ on the real axis. The ANSI standard specifies a negative imaginary part for asin on this branch cut consistent with approaching the cut from below. Evaluating (asin z) in ECL, on the other hand, returns a result with positive imaginary part if the imaginary part of z is +0.0 (consistent with approaching the cut from above) and a result with negative imaginary part if the imaginary part of z is -0.0 (consistent with approaching the cut from below)2. This applies to sqrt, asin, acos, atan, asinh, acosh and atanh.


2.10.6 Dictionary

Constant: ext:{short,single,double,long}-float-{positive,negative}-infinity

Constant positive/negative infinity for the different floating point types.

Function: ext:nan

Returns a double float NaN value. Coerce to other floating point types to get NaN values e.g. for single floats.

Function: ext:float-infinity-p x
Function: ext:float-nan-p x

Predicates to test if x is infinite or NaN.


2.10.7 C Reference


2.10.7.1 Number C types

Numeric C types understood by ECL

Type names

cl_fixnumfixnum
cl_index(integer 0 most-positive-fixnum)
floatshort-float, single-float
doubledouble-float
long double (*)long-floatECL_LONG_FLOAT:long-float
float _Complex (**)(complex single-float)ECL_COMPLEX_FLOAT:complex-float
double _Complex (**)(complex double-float)ECL_COMPLEX_FLOAT:complex-float
long-double _Complex (**)(complex long-float)ECL_COMPLEX_FLOAT:complex-float
uint8_t(unsigned-byte 8)ecl_uint8_t
int8_t(signed-byte 8)ecl_int8_t
uint16_t(unsigned-byte 16)ecl_uint16_t:uint16-t
int16_t(signed-byte 16)ecl_int16_t:int16-t
uint32_t(unsigned-byte 32)ecl_uint32_t:uint32-t
int32_t(signed-byte 32)ecl_int32_t:int32-t
uint64_t(unsigned-byte 64)ecl_uint64_t:uint64-t
int64_t(signed-byte 64)ecl_int64_t:int64-t
short(integer ffi:c-short-min ffi:c-short-max)
unsigned short(integer 0 ffi:c-ushort-max)
int(integer ffi:c-int-min ffi:c-int-max)
unsigned int(integer 0 ffi:c-uint-max)
long(integer ffi:c-long-min ffi:c-long-max)
unsigned long(integer 0 ffi:c-long-max)
long long(integer ffi:c-long-long-min ffi:c-long-long-max)ecl_long_long_t:long-long
unsigned long long(integer 0 ffi:c-ulong-long-max)ecl_ulong_long_t:ulong-long

Description

The table above shows the relation between C types and the equivalent Common Lisp types. All types are standard C99 types, except for two. First, cl_fixnum is the smallest signed integer that can fit a fixnum. Second, cl_index is the smallest unsigned integer that fits a fixnum and is typically the unsigned counterpart of cl_fixnum.

(*) DEPRECATED Previous versions of ECL supported compilers that did not define the long double type. The ECL_LONG_DOUBLE macro and long-double features indicating whether support for long double was available are removed now.

(**) The <float> _Complex types do not exist on all platforms. When they exist the macro ECL_COMPLEX_FLOAT will be defined.

Many other types might also not exist on all platforms. This includes not only long long and unsigned long long, but also some of the C99 integer types. There are two ways to detect which integer types are available in your system:

  • Check for the definition of C macros with a similar name, shown in the fifth column above.
  • In Lisp code, check for the presence of the associated features, shown in the fourth column above.

2.10.7.2 Number constructors

Creating Lisp types from C numbers

Functions

Function: cl_object ecl_make_fixnum (cl_fixnum n)
Function: cl_object ecl_make_integer (cl_fixnum n)
Function: cl_object ecl_make_unsigned_integer (cl_index n)
Function: cl_object ecl_make_single_float (float n)
Function: cl_object ecl_make_double_float (double n)
Function: cl_object ecl_make_long_float (long double n)
Function: cl_object ecl_make_csfloat (float _Complex n)
Function: cl_object ecl_make_cdfloat (double _Complex n)
Function: cl_object ecl_make_clfloat (long double _Complex n)
Function: cl_object ecl_make_uint8_t (uint8_t n)
Function: cl_object ecl_make_int8_t (int8_t n)
Function: cl_object ecl_make_uint16_t (uint16_t n)
Function: cl_object ecl_make_int16_t (int16_t n)
Function: cl_object ecl_make_uint32_t (uint32_t n)
Function: cl_object ecl_make_int32_t (int32_t n)
Function: cl_object ecl_make_uint64_t (uint64_t n)
Function: cl_object ecl_make_int64_t (int64_t n)
Function: cl_object ecl_make_short_t (short n)
Function: cl_object ecl_make_ushort_t (unsigned short n)
Function: cl_object ecl_make_int (int n)
Function: cl_object ecl_make_uint (unsigned int n)
Function: cl_object ecl_make_long (long n)
Function: cl_object ecl_make_ulong (unsigned long n)
Function: cl_object ecl_make_long_long (long long n)
Function: cl_object ecl_make_ulong_long (unsigned long long n)
Function: cl_object ecl_make_ratio (cl_object numerator, cl_object denominator)
Function: cl_object ecl_make_complex (cl_object real, cl_object imag)

Description

These functions create a Lisp object from the corresponding C number. If the number is an integer type, the result will always be an integer, which may be a bignum. If on the other hand the C number is a float, double or long double, the result will be a float.

There is some redundancy in the list of functions that convert from cl_fixnum and cl_index to lisp. On the one hand, ecl_make_fixnum always creates a fixnum, dropping bits if necessary. On the other hand, ecl_make_integer and ecl_make_unsigned_integer faithfully convert to a Lisp integer, which may be a bignum.

Note also that some of the constructors do not use C numbers. This is the case of ecl_make_ratio and ecl_make_complex, because they are composite Lisp types. When c99 complex float support is built in ecl_make_complex will use C number for float types.

These functions or macros signal no errors.


2.10.7.3 Number accessors

Unchecked conversion from Lisp types to C numbers

Functions

Function: cl_fixnum ecl_fixnum (cl_object n)
Function: float ecl_single_float (cl_object n)
Function: double ecl_double_float (cl_object n)
Function: long double ecl_long_float (cl_object n)
Function: float _Complex ecl_csfloat (cl_object n)
Function: double _Complex ecl_cdfloat (cl_object n)
Function: long double _Complex ecl_clfloat (cl_object n)

Description

These functions and macros extract a C number from a Lisp object. They do not check the type of the Lisp object as they typically just access directly the value from a C structure.


2.10.7.4 Number coercion

Checked conversion from Lisp types to C numbers

Functions

Function: cl_fixnum ecl_to_fixnum (cl_object n);
Function: cl_index ecl_to_unsigned_integer (cl_object n);
Function: float ecl_to_float (cl_object n);
Function: double ecl_to_double (cl_object n);
Function: long double ecl_to_long_double (cl_object n);
Function: float _Complex ecl_to_csfloat (cl_object n);
Function: double _Complex ecl_to_cdfloat (cl_object n);
Function: long double _Complex ecl_to_clfloat (cl_object n);
Function: uint8_t ecl_to_uint8_t (cl_object n);
Function: int8_t ecl_to_int8_t (cl_object n);
Function: uint16_t ecl_to_uint16_t (cl_object n);
Function: int16_t ecl_to_int16_t (cl_object n);
Function: uint32_t ecl_to_uint32_t (cl_object n);
Function: int32_t ecl_to_int32_t (cl_object n);
Function: uint64_t ecl_to_uint64_t (cl_object n);
Function: int64_t ecl_to_int64_t (cl_object n);
Function: short ecl_to_short (cl_object n);
Function: unsigned short ecl_to_ushort (cl_object n);
Function: int ecl_to_int (cl_object n);
Function: unsigned int ecl_to_uint (cl_object n);
Function: long ecl_to_long (cl_object n);
Function: unsigned long ecl_to_ulong (cl_object n);
Function: long long ecl_to_long_long (cl_object n);
Function: unsigned long long ecl_to_ulong_long (cl_object n);

Description

These functions and macros convert a Lisp object to the corresponding C number type. The conversion is done through a coercion process which may signal an error if the argument does not fit the expected type.


2.10.7.5 ANSI dictionary

Common Lisp and C equivalence

Lisp symbolC function
=cl_object cl_E(cl_narg narg, ...)
/=cl_object cl_NE(cl_narg narg, ...)
<cl_object cl_L(cl_narg narg, ...)
>cl_object cl_G(cl_narg narg, ...)
<=cl_object cl_LE(cl_narg narg, ...)
>=cl_object cl_GE(cl_narg narg, ...)
maxcl_object cl_max(cl_narg narg, ...)
mincl_object cl_min(cl_narg narg, ...)
minuspcl_object cl_minusp(cl_object real)
pluspcl_object cl_plusp(cl_object real)
zeropcl_object cl_zerop(cl_object number)
floorcl_object cl_floor(cl_narg narg, cl_object number, ...)
ffloorcl_object cl_ffloor(cl_narg narg, cl_object number, ...)
ceilingcl_object cl_ceiling(cl_narg narg, cl_object number, ...)
fceilingcl_object cl_fceiling(cl_narg narg, cl_object number, ...)
truncatecl_object cl_truncate(cl_narg narg, cl_object number, ...)
ftruncatecl_object cl_ftruncate(cl_narg narg, cl_object number, ...)
roundcl_object cl_round(cl_narg narg, cl_object number, ...)
froundcl_object cl_fround(cl_narg narg, cl_object number, ...)
sincl_object cl_sin(cl_object radians)
coscl_object cl_cos(cl_object radians)
tancl_object cl_tan(cl_object radians)
asincl_object cl_asin(cl_object number)
acoscl_object cl_acos(cl_object number)
atancl_object cl_atan(cl_narg narg, cl_object number1, ...)
sinhcl_object cl_sinh(cl_object number)
coshcl_object cl_cosh(cl_object number)
tanhcl_object cl_tanh(cl_object number)
asinhcl_object cl_asinh(cl_object number)
acoshcl_object cl_acosh(cl_object number)
atanhcl_object cl_atanh(cl_object number)
*cl_object cl_X(cl_narg narg, ...)
+cl_object cl_P(cl_narg narg, ...)
-cl_object cl_M(cl_narg narg, cl_object number, ...)
/cl_object cl_N(cl_narg narg, cl_object number, ...)
1+cl_object cl_1P(cl_object number)
1-cl_object cl_1M(cl_object number)
abscl_object cl_abs(cl_object number)
evenpcl_object cl_evenp(cl_object integer)
oddpcl_object cl_oddp(cl_object integer)
expcl_object cl_exp(cl_object number)
exptcl_object cl_expt(cl_object base, cl_object power)
gcdcl_object cl_gcd(cl_narg narg, ...)
lcmcl_object cl_lcm(cl_narg narg, ...)
logcl_object cl_log(cl_narg narg, cl_object number, ...)
modcl_object cl_mod(cl_object number, cl_object divisor)
remcl_object cl_rem(cl_object number, cl_object divisor)
signumcl_object cl_signum(cl_object number)
sqrtcl_object cl_sqrt(cl_object number)
isqrtcl_object cl_isqrt(cl_object natural)
make-random-statecl_object cl_make_random_state(cl_narg narg, ...)
randomcl_object cl_random(cl_narg narg, cl_object limit, ...)
random-state-pcl_object cl_random_state_p(cl_object object)
numberpcl_object cl_numberp(cl_object object)
ciscl_object cl_cis(cl_object radians)
complexcl_object cl_complex(cl_narg narg, cl_object realpart, ...)
complexpcl_object cl_complexp(cl_object object)
conjugatecl_object cl_conjugate(cl_object number)
phasecl_object cl_phase(cl_object number)
realpartcl_object cl_realpart(cl_object number)
imagpartcl_object cl_imagpart(cl_object number)
upgraded-complex-part-typecl_object cl_upgraded_complex_part_type(cl_narg narg, cl_object typespec, ...)
realpcl_object cl_realp(cl_object object)
numeratorcl_object cl_numerator(cl_object rational)
denominatorcl_object cl_denominator(cl_object rational)
rationalcl_object cl_rational(cl_object number)
rationalizecl_object cl_rationalize(cl_object number)
rationalpcl_object cl_rationalp(cl_object object)
ashcl_object cl_ash(cl_object integer, cl_object count)
integer-lengthcl_object cl_integer_length(cl_object integer)
integerpcl_object cl_integerp(cl_object object)
parse-integercl_object cl_parse_integer(cl_narg narg, cl_object string, ...)
boolecl_object cl_boole(cl_object op, cl_object integer1, cl_object integer2)
logandcl_object cl_logand(cl_narg narg, ...)
logandc1cl_object cl_logandc1(cl_object integer1, cl_object integer2)
logandc2cl_object cl_logandc2(cl_object integer1, cl_object integer2)
logeqvcl_object cl_logeqv(cl_narg narg, ...)
logiorcl_object cl_logior(cl_narg narg, ...)
lognandcl_object cl_lognand(cl_object integer1, cl_object integer2)
lognorcl_object cl_lognor(cl_object integer1, cl_object integer2)
lognotcl_object cl_lognot(cl_object integer)
logorc1cl_object cl_logorc1(cl_object integer1, cl_object integer2)
logorc2cl_object cl_logorc2(cl_object integer1, cl_object integer2)
logxorcl_object cl_logxor(cl_narg narg, ...)
logbitpcl_object cl_logbitp(cl_object index, cl_object integer)
logcountcl_object cl_logcount(cl_object integer)
logtestcl_object cl_logtest(cl_object integer1, cl_object integer2)
bytecl_object cl_byte(cl_object size, cl_object position)
bytes-sizecl_object cl_byte_size(cl_object bytespec)
byte-positioncl_object cl_byte_position(cl_object bytespec)
deposit-fieldcl_object cl_deposit_field(cl_object newbyte, cl_object bytespec, cl_object integer)
dpbcl_object cl_dpb(cl_object newbyte, cl_object bytespec, cl_object integer)
ldbcl_object cl_ldb(cl_object bytespec, cl_object integer)
ldb-testcl_object cl_ldb_test(cl_object bytespec, cl_object integer)
mask-fieldcl_object cl_mask_field(cl_object bytespec, cl_object integer)
decode-floatcl_object cl_decode_float(cl_object float)
scale-floatcl_object cl_scale_float(cl_object float, cl_object integer)
float-radixcl_object cl_float_radix(cl_object float)
float-signcl_object cl_float_sign(cl_narg narg, cl_object float1, ...)
float-digitscl_object cl_float_digits(cl_object float)
float-precisioncl_object cl_float_precision(cl_object float)
integer-decode-floatcl_object cl_integer_decode_float(cl_object float)
floatcl_object cl_float(cl_narg narg, cl_object number, ...)
floatpcl_object cl_floatp(cl_object object)
arithmetic-error-operands[Only in Common Lisp]
arithmetic-error-operation[Only in Common Lisp]

Footnotes

(1)

The main reason for this is that some numeric functions for C complex numbers return mathematically incorrect values, for example sinh(i*∞) returns i*NaN instead of the mathematically correct i*∞. Keeping this consistent with our own implementation of complex arithmetic that is used when C complex numbers are not available would require to much work. Furthermore, complex arithmetic with infinities is unreliable anyway, since it quickly leads to NaN values (consider i*∞ = (0+i*1)*(∞+i*0) = NaN+i*∞; even this simple example is already mathematically incorrect).

(2)

The reason for this behaviour is twofold: first, the approach taken by ECL is mathematically more sensible in a number system with signed zero and second, it is consistent with the specification of multi-valued complex functions in the C programming language.