Next: objects integers, Previous: objects symbols, Up: objects [Index]
Bignums are multi–precision exact integers bigger than fixnums; they
are implemented using the mpn
API of GNU GMP,
See (gmp)Low-level Functions.
Vicare only uses a bignum to represent an exact integer when the value does not fit in a fixnum; it follows that the following ranges are enforced:
negative bignums < (least-fixnum) <= all fixnums all fixnums <= (greatest-fixnum) < positive bignums
and notice that:
(+ 1 (greatest-fixnum))
which can
also be computed as (- (least-fixnum))
.
(- (least-fixnum) 1)
.
A bignum is a variable length memory block referenced by machine words tagged as vectors. The first machine word of a bignum block is tagged has bignum in its least significant bits; then comes a sign bit, zero if positive; the remaining most significant bits represent the number of words in the memory block after the first one.
|------------------------|-------------| reference to bignum heap pointer vector tag sign bit |----------------------|-|-------------| bignum first word number of words bignum tag
A pointer to the second machine word in a bignum memory block is the
pointer of type mp_limb_t
accepted by the mpn_
functions
of GMP; limb, in GMP jargon, is a machine word holding a
portion of multi–precision number. The layout of a bignum memory block
is as follows:
|----|-----|-----|-----|-----| ... 1st limb0 limb1 limb2 limb3
where the first word 1st
is a header of meta informations encoded
as explained above and each limb is a machine word stored in
native endianness; the big number is the concatenation of limbs
with limb0
being the least significant one. There is always at
least one limb; when a bignum is composed of a single limb, its value is
always non–zero and outside the range of fixnums.
To allocate a bignum we must know the number of required limbs:
ikpcb_t * pcb = ik_the_pcb(); ikuword_t nlimbs = the_number_of_limbs; ikuword_t block_size = disp_bignum_data + nlimbs * wordsize; ikuword_t align_size = IK_ALIGN(block_size); ikptr_t s_bn = ik_safe_alloc(pcb, align_size) | vector_tag;
we must explicitly build and encode the first word; the number of limbs is encoded as follows:
ikuword_t nlimbs = the_number_of_limbs; ikptr_t meta_nlimbs = nlimbs << bignum_nlimbs_shift;
the sign bit is encoded as follows:
ikuword_t sign = zero_if_positive_one_if_negative; ikptr_t meta_sign = sign << bignum_sign_shift;
and the full first word is composed and stored as follows:
ikptr_t s_bn = the_bignum; ikptr_t meta_nlimbs = ...; ikptr_t meta_sign = ...; ikptr_t s_fst = meta_nlimbs | meta_sign | bignum_tag; IK_REF(s_bn, off_bignum_tag) = s_fst;
To identify an object as bignum we do:
ikptr_t X = the_object; if ((vector_tag == IK_TAGOF(X)) && (bignum_tag == (bignum_mask & IK_REF(X, off_bignum_tag)))) it_is_a_bignum(); else it_is_not();
to extract meta informations we must first extract the first word:
ikptr_t s_bn = the_bignum; ikptr_t s_fst = IK_REF(s_bn, off_bignum_tag); ikuword_t nlimbs; ikuword_t meta_sign; nlimbs = ((ikuword_t)s_fst) >> bignum_nlimbs_shift; meta_sign = ((ikuword_t)s_fst) & bignum_sign_mask;
if meta_sign
is zero the bignum is positive, else it is negative.
To acquire a pointer to the data area we do:
ikptr_t s_bn = the_bignum; mp_limb_t * data = IK_BIGNUM_DATA_LIMBP(s_bn);
to extract the N-th limb we do:
ikptr_t s_bn = the_bignum; ikuword_t N = the_index; mp_limb_t limb = IK_LIMB(s_bn, N);
bignum_mask
is the bit pattern used to isolate a bignum tag from
an ikptr_t
value; bignum_tag
is the tag of ikptr_t
values used
as first words in bignum memory blocks.
A bit pattern used to isolate the sign bit from the ikptr_t
value
used as first word in bignum memory blocks.
The number representing the bit left–shift offset of the sign bit in
the ikptr_t
value used as first word in bignum memory blocks.
The number representing the bit left–shift offset of the number of
limbs in the ikptr_t
value used as first word in bignum memory
blocks.
Offset in bytes of the first word in a bignum memory block from the beginning of the block. It is zero.
Offset in bytes of the first byte in the data area of a bignum memory block from the beginning of the block.
Number to add to a tagged ikptr_t
reference to bignum to obtain a
pointer to the first word in a bignum memory block.
Number to add to a tagged ikptr_t
reference to bignum to obtain a
pointer to the first byte in the data area of bignum memory block.
Given a number of limbs: evaluate to the aligned size of the memory block needed to hold the bignum.
Given a number of limbs: allocate, using ik_safe_alloc()
, the
memory block needed to hold the bignum and return an untagged pointer to
it.
Given a number of limbs and the encoded sign bit: evaluate to the first word of a bignum object. It is equivalent to the following:
ikptr_t meta_nlimbs; ikptr_t s_fst; meta_nlimbs = (nlimbs << bignum_nlimbs_shift) s_fst = meta_nlimbs | meta_sign | bignum_tag;
Given a number of limbs evaluate to the corresponding first word of bignum representing a positive or negative number.
Given a reference to bignum: evaluate to the location of the first word holding meta informations. Can be used both as operand or left–side of assignment:
ikptr_t s_bn = the_bignum; ikptr_t s_fst; s_fst = IK_BIGNUM_FIRST(s_bn); IK_BIGNUM_FIRST(s_bn) = s_fst;
Given a reference to bignum: evaluate to the location of the N-th limb in the data area. Can be used both as operand or left–side of assignment:
ikptr_t s_bn = the_bignum; mp_limb_t limb; limb = (mp_limb_t)IK_LIMB(s_bn); IK_LIMB(s_bn) = (ikptr_t)limb;
Given a reference to bignum: evaluate to a pointer to the first byte in the data area, which is a pointer to the least significant limb.
Given a reference to bignum: evaluate to the least significant limb in the data area.
Given a reference to bignum and its number of limbs: evaluate to the most significant limb in the data area.
Next: objects integers, Previous: objects symbols, Up: objects [Index]