Now creating the correct internal bignum representation

add_integers returns an integer which by inspection of the internal representation is correct, but the print representation is not correct.
This commit is contained in:
Simon Brooke 2019-01-03 11:21:08 +00:00
parent 4295b6e57f
commit d9d789fdd0
5 changed files with 54 additions and 27 deletions

View file

@ -4,3 +4,5 @@
(cond
((= x 1) n)
(t (* n (expt n (- x 1)))))))
(expt 2 65)

View file

@ -2,6 +2,6 @@
Each integer comprises at least one cell of type INTR, holding a signed 64 bit integer with a value in the range 0 ... MAX-INTEGER, where the actual value of MAX-INTEGER does not need to be the same as the C language LONG\_MAX, provided that it is less than this. It seems to me that a convenient number would be the largest number less than LONG\_MAX which has all bits set
LONG\_MAX is 0x7FFFFFFFFFFFFFFF, so the number we're looking for is 0xFFFFFFFFFFFFFFF, which is 1,152,921,504,606,846,975, which is 2^60 - 1. This means we can use bit masking with 0xFFFFFFFFFFFFFFF to extract the part of **int64_t** which will fit in a single cell.
LONG\_MAX is 0x7FFFFFFFFFFFFFFF, so the number we're looking for is 0x0FFFFFFFFFFFFFFF, which is 1,152,921,504,606,846,975, which is 2^60 - 1. This means we can use bit masking with 0xFFFFFFFFFFFFFFF to extract the part of **int64_t** which will fit in a single cell.
It also means that if we multiply two **int64_t**s into an **__int128_t**, we can then right-shift by 60 places to get the carry.

View file

@ -31,7 +31,7 @@
/*
* The maximum value we will allow in an integer cell.
*/
#define MAX_INTEGER ((__int128_t)0xFFFFFFFFFFFFFFF)
#define MAX_INTEGER ((__int128_t)0x0fffffffffffffffL)
/**
* hexadecimal digits for printing numbers.
@ -109,9 +109,9 @@ struct cons_pointer add_integers( struct cons_pointer a,
if ( integerp( a ) && integerp( b ) ) {
debug_print( L"add_integers: ", DEBUG_ARITH );
debug_print_object( a, DEBUG_ARITH );
debug_print( L" x ", DEBUG_ARITH );
debug_print( L" + ", DEBUG_ARITH );
debug_print_object( b, DEBUG_ARITH );
debug_printf( DEBUG_ARITH, L"; carry = %ld\n", carry );
debug_println( DEBUG_ARITH);
while ( !nilp( a ) || !nilp( b ) || carry != 0 ) {
__int128_t av =
@ -133,16 +133,20 @@ struct cons_pointer add_integers( struct cons_pointer a,
rv = rv & MAX_INTEGER;
}
struct cons_pointer tail = make_integer( (int64_t)(rv << 64), NIL);
struct cons_pointer tail = make_integer( (int64_t)rv, NIL);
if (nilp(cursor)) {
cursor = tail;
} else {
inc_ref(tail);
/* yes, this is a destructive change - but the integer has not yet been released
* into the wild */
struct cons_space_object * c = &pointer2cell(cursor);
c->payload.integer.more = tail;
inc_ref(tail);
/* yes, this is a destructive change - but the integer has not yet been released
* into the wild */
struct cons_space_object * c = &pointer2cell(cursor);
c->payload.integer.more = tail;
}
if ( nilp(result) ) {
result = cursor;
}
a = pointer2cell( a ).payload.integer.more;
@ -150,7 +154,7 @@ struct cons_pointer add_integers( struct cons_pointer a,
}
}
debug_print( L"add_integers returning: ", DEBUG_ARITH );
debug_print_object( result, DEBUG_ARITH );
debug_dump_object( result, DEBUG_ARITH );
debug_println( DEBUG_ARITH );
return result;
@ -167,10 +171,10 @@ struct cons_pointer multiply_integers( struct cons_pointer a,
__int128_t carry = 0;
if ( integerp( a ) && integerp( b ) ) {
debug_print( L"multiply_integers: ", DEBUG_ARITH );
debug_print_object( a, DEBUG_ARITH );
debug_print( L" x ", DEBUG_ARITH );
debug_print_object( b, DEBUG_ARITH );
debug_print( L"multiply_integers: \n", DEBUG_ARITH );
debug_dump_object( a, DEBUG_ARITH );
debug_print( L" x \n", DEBUG_ARITH );
debug_dump_object( b, DEBUG_ARITH );
debug_println( DEBUG_ARITH );
while ( !nilp( a ) || !nilp( b ) || carry != 0 ) {
@ -196,19 +200,19 @@ struct cons_pointer multiply_integers( struct cons_pointer a,
debug_printf( DEBUG_ARITH,
L"multiply_integers: 64 bit overflow; setting carry to %ld\n",
(int64_t)carry );
rv = rv & MAX_INTEGER;
rv &= MAX_INTEGER; // <<< PROBLEM IS HERE!
}
struct cons_pointer tail = make_integer( (int64_t)(rv << 64), NIL);
struct cons_pointer tail = make_integer( (int64_t)rv, NIL);
if (nilp(cursor)) {
cursor = tail;
} else {
inc_ref(tail);
/* yes, this is a destructive change - but the integer has not yet been released
* into the wild */
struct cons_space_object * c = &pointer2cell(cursor);
c->payload.integer.more = tail;
inc_ref(tail);
/* yes, this is a destructive change - but the integer has not yet been released
* into the wild */
struct cons_space_object * c = &pointer2cell(cursor);
c->payload.integer.more = tail;
}
if ( nilp(result) ) {
@ -220,8 +224,8 @@ struct cons_pointer multiply_integers( struct cons_pointer a,
}
}
debug_print( L"multiply_integers returning: ", DEBUG_ARITH );
debug_print_object( result, DEBUG_ARITH );
debug_print( L"multiply_integers returning:\n", DEBUG_ARITH );
debug_dump_object( result, DEBUG_ARITH );
debug_println( DEBUG_ARITH );
return result;
@ -260,7 +264,11 @@ struct cons_pointer integer_to_string( struct cons_pointer int_pointer,
accumulator = llabs( accumulator );
int digits = 0;
if ( accumulator == 0 ) {
if ( accumulator == 0 && !nilp(integer.payload.integer.more) ) {
accumulator = MAX_INTEGER;
}
if ( accumulator == 0) {
result = c_string_to_lisp_string( L"0" );
} else {
while ( accumulator > 0 ) {

View file

@ -161,6 +161,9 @@ struct cons_pointer read_number( struct stack_frame *frame,
wint_t initial, bool seen_period ) {
debug_print( L"entering read_number\n", DEBUG_IO );
struct cons_pointer result = NIL;
/* TODO: accumulator and dividend cannot be `int64_t`s, otherwise we cannot
* read bignums. They will have to be Lisp integers. */
int64_t accumulator = 0;
int64_t dividend = 0;
int places_of_decimals = 0;

14
unit-tests/bignum.sh Normal file
View file

@ -0,0 +1,14 @@
#!/bin/bash
expected='1,152,921,504,606,846,976'
# 1,152,921,504,606,846,975 is the largest single cell positive integer;
# consequently 1,152,921,504,606,846,976 is the first two cell positive integer.
actual=`echo '(+ 1,152,921,504,606,846,975 1)' | target/psse -v 68 2>bignum.log | tail -1`
if [ "${expected}" = "${actual}" ]
then
echo "OK"
else
echo "Fail: expected '${expected}', got '${actual}'"
exit 1
fi