biglib13

advertisement
Wesley Loewer's Big Numbers v 1.1
(C) 1994-95 Wesley B. Loewer
The Big Number Library is a set of routines that allows one to develop
programs which calculate numbers to an arbitrary precision, limited only
by the memory available.
The library differs from most other big number libraries in that it has
both floating point and fixed point math available. Fix point math is
faster, but is limited in its range. Some libraries offer big integer
math, but this differs from fixed point math too. With fix point math
the
most significant digits are kept after a multiplication, but with integer
math, the least significant digits are kept. Certain applications work
very well with fixed point math, such as calculating some fractal images.
This purpose of writing this Big Number Library was originally to give
Fractint the ability to zoom arbitrarily deep. After having accomplished
this, it was suggested that others may be interested in these routines.
Feel free to use this library of routines in your own programs.
I ask is
All that
1) You give proper credit in the documentation.
2) You send me a complimentary (registered) copy of your program.
Thanks,
Wesley Loewer
78 S. Circlewood Glen
The Woodlands, TX 77381
USA
(713) 292-3449
loewer@tenet.edu
-------------------------------------------------------------------Wesley Loewer wrote the assembler and most of the C code.
Timothy Wegner did much of the debugging while encorporating it into
Fractint and wrote an assortment of valuable routines. He also brought
in a lot of common sense into the project.
Ken Shirriff provided valuable suggestions for porting the C code over
to other platforms, especially byte order issues (Big vs Little Endian).
-------------------------------------------------------------------There are several parts to the bignum library:
biginit.c - Initialization routines.
version of biginit.c.)
(Fractint uses a separate, custom
bignum.c - General routines, routines that would not be speeded up much
with assembler, particularly for the bn_t format numbers.
bigflt.c -
Routines that would not be speeded up much with assembler,
particularly for the bf_t format numbers.
bignuma.asm - Hand coded assembler routines.
(not included with fractint) contains:
fbignuma.asm
bbignuma.asm
nbignuma.asm
segment.
pbignuma.asm
The full bignum library
(far) - any big number can reside in any segment.
(based) - all big numbers must reside in any one segment.
(near) - all big numbers must reside in default data
(protected) - for protected mode flat memory model.
(work still in progress)
The descriptors (far, based, near) describe the location of the big
numbers, not the memory model used to compile/assemble the code. All
versions of the assembler code require a memory model with near data
(ie: small or medium), although the stack segment (SS) does not have
to
be the same as the default data segment (DS).
bignumc.c - Portable C versions of routines in bignuma.asm. As C code
in
bignum.c or bigflt.c is ported to assembler in bignuma.asm, the C code
should be moved to bignumc.c.
bigmisc.c - Provides necessary routines that may not be supplied by the
standard compiler libraries. These include long double math routines,
ltoa(), memmove(). Only use these if necessary.
-------------------------------------------------------------------Portability Notes:
When compiling the bignum library, there are a number of macros that
can be defined which affect compilation.
BIG_MODEL (BIG_NEAR (DOS default), BIG_BASED, BIG_FAR, BIG_HUGE)
These three choices pertain to DOS platforms only. One of these must
be used if the assembler code is being used. If the C code is used,
then one of these or BIG_ANSI_C must be used. The near, based, far,
and huge refer to the size of the bignum pointers, not the memory
model used to compile the code. BIG_BASED can only be used on a
compiler that supports based pointers. (BIGNUM.H)
BIG_MODEL (BIG_ANSI_C (non-DOS default))
Pointer modifiers (near, based, far) are not used. On a DOS machine,
the bignum pointer size is determined by the memory model used. On
non-DOS platforms, this option is required and is usually set
automatically. USE_BIGNUM_C_CODE is automatically defined when
BIG_ANSI_C is defined. (BIGNUM.H)
USE_BIGNUM_C_CODE
If defined, the assembler code is not used. It can be used on DOS
machines and must be used on non-DOS machines. Using the C code
on DOS machines allows for any memory model to be used. If the
ASM code is used, then only small and medium memory models can be
used. On DOS machines, this options works with any of the above
options. (BIGPORT.H)
BYTE_ORDER
If the value of BYTE_ORDER is 1234, it is compiled for a Little
Endian
machine (Intel 80x86). The value 4321 compiles for a Big Endian
machine. DOS, Linux machines are assumed to be Little Endian while
Amiga and unix machines defaults to Big Endian. (BIGPORT.H)
ACCESS_BY_BYTE
This is automatically set when BYTE_ORDER is set for Big Endian
machines. Some Little Endian machines may require that memory access
occur only on certain alignments. Such a machine requires that
ACCESS_BY_BYTE be set. (BIGPORT.H)
DO_NOT_USE_LONG_DOUBLE
The bignum library will use long double floating point numbers if
they
are supported. This is usually detected automatically by the code.
However, some compilers only partially support long doubles in that
they have long doubles, but they can't be used with sprintf() and
sscanf(). If the long double gives trouble, then define this macro.
(BIGPORT.H)
Also, there is a program included called datainfo.c that will give you
information about your system. This may help you determine what macro
options to use.
-------------------------------------------------------------------Big Number Library
The bignumber format is simply a signed integer of variable length. The
bytes are stored in reverse order (least significant byte first, most
significant byte last). The sign bit is the highest bit of the most
significant byte. Negatives are stored in 2's complement form. The byte
length of the bignumbers must be a multiple of 4 for 386+ operations, and
a multiple of 2 for 8086/286 and non 80x86 machines.
Some of the arithmetic operations coded here may alter some of the
operands used. Therefore, take note of the SIDE-EFFECTS listed with each
procedure. If the value of an operand needs to be retained, just use
copy_bn() first. This was done for speed sake to avoid unnecessary
copying. If space is at such a premium that copying it would be
difficult, some of the operations only change the sign of the value. In
this case, the original could be optained by calling neg_a_bn().
Most of the bignumber routines operate on true integers. Some of the
procedures, however, are designed specifically for fixed decimal point
operations. The results and how the results are interpreted depend on
where the implied decimal point is located. The routines that depend on
where the decimal is located are: strtobn(), bntostr(), bntoint(),
inttobn(),
bntofloat(), floattobn(), inv_bn(), div_bn(). The number of bytes
designated for the integer part may be 1, 2, or 4.
It's a good idea to use a few more significant than what is actually
required. The big fixed point format (see below) is best used when only
addition, subtraction, and multiplication are needed. Because of the
limitations of a fixed point format, if division, trigonometric, or
logarithmic functions are needed, it is best to use the floating point
big
number format.
-------------------------------------------------------------------Big Fixed Point Number Format:
BIGNUMBER FORMAT:
The following is a discription of the bignumber format and associated
variables. The number is stored in reverse order (Least Significant
Byte,
LSB, stored first in memory, Most Significant Byte, MSB, stored last).
Each '_' below represents a block of memory used for arithmetic (1 block
=
4 bytes on 386+, 2 bytes on 286-). All lengths are given in bytes.
LSB
MSB
_ _ _ _ _ _ _ _ _ _ _ _
n <----------- bnlength ----------->
intlength ---> <--bnlength = the length in bytes of the bignumber
intlength = the number of bytes used to represent the integer part of
the bignumber. Possible values are 1, 2, or 4. This
determines the largest number that can be represented by
the bignumber.
intlength = 1, max value = 127.99...
intlength = 2, max value = 32,767.99...
intlength = 4, max value = 2,147,483,647.99...
FULL DOUBLE PRECISION MULTIPLICATION:
( full_mult_bn(), full_square_bn() )
The product of two bignumbers, n1 and n2, will be a result, r, which is
a double wide bignumber. The integer part will also be twice as wide,
thereby eliminating the possiblity of overflowing the number.
LSB
MSB
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
r <--------------------------- 2*bnlength ----------------------------->
2*intlength --->
<--If this double wide bignumber, r, needs to be converted to a normal,
single width bignumber, this is easily done with pointer arithmetic.
converted value starts at r+shiftfactor (where shiftfactor =
The
bnlength-intlength) and continues for bnlength bytes. The lower order
bytes and the upper integer part of the double wide number can then be
ignored.
LSB
MSB
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
r <--------------------------- 2*bnlength ----------------------------->
2*intlength --->
<--LSB
MSB
r+shiftfactor
<---------- bnlength ------------>
intlength ---> <-PARTIAL PRECISION MULTIPLICATION:
( mult_bn(), square_bn() )
In most cases, full double precision multiplication is not necessary.
The
lower order bytes are usually thrown away anyway. The non-"full"
multiplication routines only calculate rlength bytes in the result. The
value of rlength must be in the range: 2*bnlength <= rlength < bnlength.
The amount by which rlength exceeds bnlength accounts for the extra bytes
that must be multiplied so that the first bnlength bytes are correct.
These extra bytes are refered to in the code as the "padding," that is:
rlength=bnlength+padding.
All three of the values, bnlength, rlength, and therefore padding, must
be
multiples of the size of memory blocks being used for arithmetic (2 on
8086/286 and 4 on 386+). Typically, the padding is 2*blocksize. In the
case where bnlength=blocksize, padding can only be blocksize to keep
rlength from being too big.
The product of two bignumbers, n1 and n2, will then be a result, r, which
is of length rlength. The integer part will be twice as wide, thereby
eliminating the possiblity of overflowing the number.
LSB
MSB
_ _ _ _ _ _ _ _ _ _ _ _ _ _
r <---- rlength = bnlength+padding ------>
2*intlength ---> <--If r needs to be converted to a normal, single width bignumber, this is
easily done with pointer arithmetic. The converted value starts at
r+shiftfactor (where shiftfactor = padding-intlength) and continues for
bnlength bytes. The lower order bytes and the upper integer part of the
double wide number can then be ignored.
LSB
MSB
_ _ _ _ _ _ _ _ _ _ _ _ _ _
r <---- rlength = bnlength+padding ------>
2*intlength
LSB
r+shiftfactor <----------
--->
<--MSB
bnlength --------->
intlength ---> <---
-------------------------------------------------------------------Big Floating Point Number Format:
A big floating point number consists of a signed big number integer of
length bflength and of intlength 2 (see bignum.c for details) followed by
a signed 16 bit exponent. It is important to remember that the exponent
is also in a Little Endian format.
The value of the big floating point number is:
value = mantissa * 256^exponent
where the absolute value of the mantissa is in the range 1<=m<256.
Notice that this differs from the IEEE format where
value = mantissa * 2^exponent
where the absolute value of the mantissa is in the range 1<=m<2.
-------------------------------------------------------------------Big10 Floating Point Number (base 10) Format:
(Just when you thought it was safe to go back in the water.)
Just when you thought you seen every type of format possible,
16 bit integer, 32 bit integer, double, long double, mpmath,
bn_t, bf_t, I now give you bf10_t (big float base 10)!
Why, because this is the only way (I can think of) to properly do a
bftostr() without rounding errors. With out this, then
-1.9999999999( > LDBL_DIG of 9's)9999999123456789...
will round to -2.0. The good news is that we only need to do two
mathematical operations: multiplication and division by integers
bf10_t format: (notice the position of the MSB and LSB)
MSB
LSB
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
n <><------------- dec --------------><> <->
1 byte pad
1 byte rounding
2 byte exponent.
total length = dec + 4
Since this format is only used internally, it is not important to keep
the exponent in Little Endian format and can be in whatever the machines
native format is. The manitissa is accessed only one byte at a time
anyway
so byte order does not matter there either.
-------------------------------------------------------------------Trig Functions
If you plan on using any trig functions, it is important that you run the
GENBIGPI program first and copy the resulting PI256.DAT file into the
init_pi() routine in biginit.c. This calculates pi to a given number of
decimal places so that your program doesn't have to calculate it on the
fly. Make sure you specify a number of digits larger that what your
program will ever use.
Download