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.