Arbitrary Precision Math Library The Arbitrary Precision Math Library is a series of routines that allows the user to perform math to any level of accuracy that is desired. These APM entities ("APM" == "Arbitrary Precision Math") can be initialized from character strings or from long integers, and they can be converted back into character strings by a routine that allows simple formatting. With the exception of the routines that do division, the results of APM operations are guaranteed to contain enough precision to hold exact values. The APM routines will automatically allocate enough space in their results to hold the proper number of digits. The APM math is done on a new data type called "APM". This is actually a pointer to a structure, but the contents of the structure should never be manipulated: all operations on APM entities are done through a functional interface. APM items can be represented in any of 36 bases: 2 through 36 and 10000. The latter is quite useful: numbers that are represented in bases that are powers of 10 are quite easy to accurately convert to and from character strings containing their decimal representation, and 10000 is the largest power of 10 that fits into a short integer (16 bits). The base must fit into a short integer since calculations internal to the APM routines need up to twice as much storage as is needed to hold the base, and the largest unit we can deal with easily is a long integer (2 shorts). It turns out that speed improves and memory usage decreases as the magnitude of the base increases, so base 10000 is a win in both counts. I have done some informal benchmarks in which base 10000 is 6 to 10 times faster than base 10 for numbers with 15 - 20 decimal digits of precision. Although there is a multitude of bases, there is as yet no provision for conversion from one base to another, except for the special case of converting between base-10 character strings and base-10000 APM values. All the input APM parameters to any given APM routine must be of the same base. The caller must initialize all APM values before the routines can operate on them (including the values intended to contain results of calculations). Once this initialization is done, the user never needs to worry about resizing the APM values, as this is handled inside the APM routines and is totally invisible to the user. The result of a APM operation cannot be one of the other APM operands. If you want this to be the case, you must put the result into a temporary variable and then assign it to the appropriate operand. All of the routines set the value of a global integer called "apm_errno" to an error status: 0 means the operation succeeded, > 0 means there was a warning but that there still is a result, and < 0 means there was an error which prohibited the operation from completing. Except where otherwise noted, the routines return this same status value. The caller has the option of registering a handler for errors and warnings. If this has been done, the handler will be invoked as each APM routine returns, and it will be passed appropriate information as to the status of the call to this APM routine. With a registered error handler it is not necessary for the caller to check the error code after each call to a APM routine. For ease of debugging, each APM routine is actually a macro that saves the file name and line number and then calls a function of a slightly different name. This allows the error handler to report the exact location of the APM operation which caused the error. The macros you will use have names of the form "apmRoutineName". To get the corresponding actual function name (say, when using a debugger), replace all capital letters with an underbar ("_") followed by its lower-case counterpart. For example, for a hypothetical routine called "apmRoutineName", the actual function will be named "apm_routine_name". You should never explicitly call the actual function. You should avoid using symbols that start with the characters "APM_" and "apm_", as they may clash with symbols that already exist in the APM Library. There is one routine for each basic arithmetic operation such as adding, subtracting, etc. This can become cumbersome when the result of a complicated expression is desired. Therefore, I have added a routine that will perform a series of operations in the manner of an RPN ("Reverse Polish Notation") calculator. See the routine 'apmCalc' (below) for more details. In my original version of this library, I discovered that a great deal of system overhead was wasted allocating and deallocating APM values. So, I adopted what has turned out to be a noticibly faster allocation scheme: newly allocated APM values are stored in a list. When the user disposes of one of these, it isn't really freed: its entry in the list is marked as unused. Subsequent attempts to allocate a new APM value will make use of any existing entries in this list that are marked as unused instead of allocating a new entry. Only when there are no unused entries in the list will a new entry be allocated. There is a routine that can be called which will perform a garbage collection: i.e., it actually frees all unused APM entries (see 'apmGarbageCollect', below). A file called apm.h must be included in all programs that use the APM routines. It defines the "APM" data type, the error codes, the apm_errno variable, and several other things. This software was written in as portable a manner as possible. I have gotten it running successfully under several environments: on a Sun 3/xxx using SunOS 3.5 and 4.0, on an IBM RT running under AIX, under MSDOS on an IBM PC using Microsoft C version 5.1 and Turbo C version 1.5 (or was it version 2.0?). There are two makefiles: the one called "Makefile" is the unix version ... it works under SunOS 3.5 and I presume it would be easy to alter it to work under other unix environments; the one called "makefile.msc" will build the library under MSDOS using Microsoft C version 5.1 ... it is meant to run with the make program "PC/MAKE". I'm sure that some of you will find ways of enhancing and speeding up the routines in this library. Time permitting, I will be doing that as well. I urge you to post your bug fixes and changes to the net and mail them to me at my address below, so they might be incorporated into a later version of this library. Some suggested areas of enhancement and optimization are: 1) Somehow speeding up the memory allocation scheme. 2) Some sort of floating-point/APM conversion. I left this out because of the widely varying floating-point formats that exist on the various machines these routines can and might run on, and because I intended APM to *replace* floating-point, not to work in conjunction with it. 3) Base conversion. 4) Roots, powers, logarithms, trig functions, exponential functions, etc. 5) More productions in the makefiles ("install", "rdist", etc.). 6) A fancy installation script. 7) Writing a man page for all this.