FIXED32 and COMPF32 All source code, for this math library is copyright (c) 1995, Eric Nixon. I am also known as the Vegetable Rights Activist (eat a cow instead). This fixed math library is free for private use, freeware, and public domain software. If this code, or part of it, is used for profit, a small fee of $15 is required for its use. If this code is used in freeware, public domain, or private use, all I ask is that you give me credit for its use. There is not a warranty of any sorts for this library, so use at own risk. Please, feel free to distribute in its original form. Donations will not be turned down. FIXED32.PAS and COMPF32.PAS are 32 bit fixed point math libraries for Borland Pascal 7.0 (Previous versions of Pascal and turbo Pascal will work, excluding extremely early versions, say 5.0 and higher, I think. Just recompile and try it.). The use of the code requires a 386 or higher CPU, there is not a checking routine to see if the CPU is a 386 or higher, this is left up for you to check. I wrote these libraries to increase speed in calculating fractals, floating point was just to slow. The majority of these functions are faster than the FPU, I admit that some of the functions are slower than the FPU, and could use some work. So, feel free to modify the code and send me a copy. Since I still only use BBS's and don't surf the internet yet, you cannot send me an E-Mail (I get too much of that a work anyway), so send your modifications to my address, listed at the end of this document. If I like your modifications, I will include it in future releases (if there are any) and give credit. FIXED32.PAS uses the LongInt type for storage of 32 bit floating point numbers. All of the math functions I could think of are here, with the exception of Factorial (I do not know how to do a non integer FACT). The majority of functions are linked to FIXED32.ASM. Note, this is my first attempt at using assembly, so don't knock it. COMPF32.PAS uses FIXED32.PAS for its fixed point math functions. This is a complex math library (a+bi). And again, I put all the complex math functions that I could think of in here. There are a few example fractal programs in Fractals.Zip. This is a list of files the should have come with 'FIXCMP32.ZIP': FIXED32.ASM Assembly module linked to FIXED32.PAS FIXED32.OBJ Object file of FIXED32.ASM FIXED32.PAS 32 bit fixed point math library FIXED32.TPU BP7 unit of FIXED32.PAS COMPF32.PAS 32 bit fixed point complex number math library COMPF32.TPU BP7 unit of COMPF32.PAS FRACTALS.ZIP Demo fractal programs that use COMPF32/FIXED32.PAS FILE_ID.DIZ Description file MATH.DOC Word 2.0 documentation file MATH.TXT This documentation file FIXED32.PAS, FIXED32.ASM, and COMPF32.PAS took about two months to put together. The reason for taking so long is simple, I had to learn assembly during the making of this library. I also had to derive many of the equations used. It probably would have taken longer if I had not noticed that my HP 28S manuals contained several of the equations that I had already derived from scratch, and some that I hadn't. I also spent a couple of weeks, verifying the output of the equations with my HP (with several modifications), which is not an easy task, because I had to do it manually. I commented the files so they are easier to follow, but sometimes I commented a little to much, but oh well what can I say. I also am not a full time coder, I'm actually an electronics engineer who codes for the fun of it. I also code because I have no life after work. FIXED32.PAS uses FIXED32.ASM for the low level routines for multiplying, dividing, trig functions, and logarithms. The higher routines are for displaying, conversions, hyperbolic, and some arc functions are written in Pascal. Notice, there is not any FixedAdd, or FixedSub functions, the reason for this is that the Pascal + and - will do. All of the functions and procedures (except the conversion functions) have a prefix of 'Fixed'. The functions that have a postfix of 'NoCheck' do not check the output for overflow and will give erroneous results. The 'NoCheck' functions are faster, but you might get in trouble when using them (div zero, or just plain garbage), so I do not recommend them. For all functions (excluding 'NoCheck' functions), when an overflow happens, the maximum number for a fixed point number is returned (kind of like returning infinity, but much smaller), these numbers are defined as MaxPos, and MaxNeg. This maximum number is either positive, or negative depending on what the actual outputs sign really is. Problems do crop up when the maximum fixed point number is returned, future calculations using this answer will be erroneous. I also ran into a problem when I added two positive maximum numbers, and got a -2 in return. So try to keep your numbers in close, and if you are doing fractals, keep the bounding number small. FixedShift is one of the most impotent numbers in this library (Did I say impotent, sorry, I meant important.). This is where the fixed binary point is located. I use a fixed point of 22. This gives me a range of +/-512, and a minimum of 2.3842E-7, which is sufficient for most calculations. Make sure that if you change FixedShift, you change it in the ASM module also. Another important number is FixedOne, which of course is equal to 1. I use FixedOne to convert real numbers at compile time instead of during execution (Execution, don't you think that is a harsh word, why not decapitated instead.). COMPF32.PAS uses FIXED32.PAS as a unit. All of the functions and procedures have a prefix of 'FixedComplex'. The functions that have a postfix of 'NoCheck' do not check the output for overflow and will give erroneous results. The 'NoCheck' functions are faster, but you might get in trouble when using them (div zero, or just plain garbage), so I do not recommend them. Not much more needs to be said about this unit, except a description of each function, which comes later in this document. If you want to know more about complex numbers, read the comments in the source code, or ask a math teacher, or consult a math book if you are interested. FIXED32.PAS: type TFixed Used for declaring a fixed point number. FIXED32.PAS: variables and constants FixedShift This is the location of the binary point. Must be 28>=x<=16. DANGER, Will Robinson, DANGER, must be the same as FixedShift in FIXED32.ASM. FixedOne Fixed point number equal to 1 FixedNearOneUp Fixed point number close to one on the upper side. This is used for my ArcTangent function. I had problems with number close to one, so I put a kluge in there to keep it on track. FixedNearOneDown Fixed point number close to one on the downside. This is used for my ArcTangent function. I had problems with number close to one, so I put a kluge in there to keep it on track. Pi Constant equal to pi. e Constant equal to e. FixedPi Fixed point number equal to pi. Fixed2Pi Fixed point number equal to 2*pi. Fixed1_2Pi Fixed point number equal to 1/(2*pi). Fixede Fixed point number equal to e. MaxPos Maximum positive number a fixed point can be. MaxNeg Maximum negative number a fixed point can be. FIXED32.PAS: procedures FixedInit; External; Called as the start up procedure in FIXED32.PAS. This function initializes the variables in FIXED32.ASM. Makes external call to FIXED32.ASM. InitRandom(Seed,Mult,Incr:TFixed); External; This initializes the random number generator in FIXED32.ASM. The random function starts with the same values every time, if you do not want this to happen you need to initialize with the timer, if you want it to be more random. Makes external call to FIXED32.ASM. FIXED32.PAS: functions RealToFixed(a:Real):TFixed; Converts a real number to a fixed number. IntToFixed(a:Integer):TFixed; Converts an integer to a fixed number. FixedToInt(a:TFixed):Integer; Converts a fixed number to an integer. FixedToReal(a:TFixed):Real; Converts a fixed number to a real number. FixedMult(a,b:TFixed):TFixed; External; Multiplies a by b. Makes external call to FIXED32.ASM. (a*b) FixedMultNoCheck(a,b:TFixed):TFixed; External; Multiplies a by b, with no over flow checking. Makes external call to FIXED32.ASM. (a*b) FixedDiv(a,b:TFixed):TFixed; External; Divides a by b. Makes external call to FIXED32.ASM. (a/b) FixedDivNoCheck(a,b:TFixed):TFixed; External; Divides a by b, with no over flow checking. Makes external call to FIXED32.ASM. (a/b) FixedDiv2(a:TFixed):TFixed; External; Since Pascal only has the SHR operator and not the SAR operator, I put this in to divide by 2. This will work with negative number now. Makes external call to FIXED32.ASM. (a/2 or (a SAR 2)) FixedInv(a:TFixed):TFixed; Inverts a. (1/a) FixedInvNoCheck(a:TFixed):TFixed; Inverts a, with no over flow checking. (1/a) FixedSqr(a:TFixed):TFixed; External; Squares a. Makes external call to FIXED32.ASM. (a^2) FixedSqrNoCheck(a:TFixed):TFixed; External; Squares a, with no over flow checking. Makes external call to FIXED32.ASM. (a^2) FixedSqrt(a:TFixed):TFixed; External; Takes the square root of the absolute value of a. Makes external call to FIXED32.ASM. (ABS(a)^(1/2)) FixedExp(a:TFixed):TFixed; External; Takes the exponential of a. Makes external call to FIXED32.ASM. (e^a) FixedLn(a:TFixed):TFixed; External; Takes the natural log of a. If a<0 then you will get MaxNeg as a return. Makes external call to FIXED32.ASM. (ln(a)) FixedSin(a:TFixed):TFixed; External; Takes the sine of a. Makes external call to FIXED32.ASM. (sin(a)) FixedASin(a:TFixed):TFixed; Takes the arcsine of a. If a>1 or a<-1 then you will get garbage, because the answer is not a real number. (arcsin(a)) FixedSinh(a:TFixed):TFixed; Takes the hyperbolic sine of a. (sinh(a)) FixedASinh(a:TFixed):TFixed; Takes the arc hyperbolic sine of a. (arcsinh(a)) FixedCos(a:TFixed):TFixed; External; Takes the cosine of a. Makes external call to FIXED32.ASM. (cos(a)) FixedACos(a:TFixed):TFixed; Takes the arccosine of a. If a>1 or a<-1 then you will get garbage, because the answer is not a real number. (arccos(a)) FixedCosh(a:TFixed):TFixed; Takes the hyperbolic cosine of a. (cos(a)) FixedACosh(a:TFixed):TFixed; Takes the arc hyperbolic cosine of a. If a<1 and a>-1 then you will get garbage, because the answer is not a real number. (arcsinh(a)) FixedTan(a:TFixed):TFixed; External; Takes the tangent of a. Makes external call to FIXED32.ASM. (tan(a)) FixedArcTan(a:TFixed):TFixed; External; Takes the arctangent of a. Very slow when a is close to one, so FixedATan uses a kluge to cover the mistake. Makes external call to FIXED32.ASM. (arctan(a)) FixedATan(a:TFixed):TFixed; Takes the arctangent of a. Does a little conversion and send to FixedArcTan. (arctan(a)) FixedTanh(a:TFixed):TFixed; Takes the hyperbolic tangent of a. (tanh(a)) FixedATanh(a:TFixed):TFixed; Takes the arc hyperbolic tangent of a. (arctanh(a)) FixedPower(a,b:TFixed):TFixed; Takes a to the power of b. (a^b using exp(b*ln(a))) FixedRandom:TFixed; Returns a fixed random number between 0 an 1. Random:LongInt; Returns a random number along the full length of LongInt, positive and negative. FIXED32.ASM: variables and constants AllSet Used for making a mask, so I can find the fractional part of a fixed number. Also used as the last two data elements in Factorial. MaxPos Returned when output becomes out of range and is positive. MaxNeg Returned when output becomes out of range and is positive. FixedShift This is the location of the binary point. Must be 28>=x<=16. DANGER, Will Robinson, DANGER, must be the same as FixedShift in FIXED32.PAS. FixedFrac Mask used to fined the fractional part of a fixed point. FixedOne Fixed point number equal to 1. FixedNegOne Fixed point number equal to -1. Fe Equals to e with a fixed point of 30. This number will be converted later to a fixed point of FixedShift. F1_e Equals to 1/e with a fixed point of 32. This number will be converted later to a fixed point of FixedShift. FPi Equals to pi (3.141592654) with a fixed point of 30. This number will be converted later to a fixed point of FixedShift. F1_Pi Equals to 1/pi with a fixed point of 32. This number will be converted later to a fixed point of FixedShift. Fixede Array of fixed point numbers equal to e^n, where n=0..10. Used to speed up the process. Fixed1_e Array of fixed point numbers equal to 1/e^n, where n=0..20. Used to speed up the process. FixedPi Fixed point number equal to pi Fixed1_Pi Fixed point number equal to 1/pi Fixed2Pi Fixed Point number equal to 2*pi Fixed1_2Pi Fixed Point number equal to 1/(2*pi) Factorial Array of fixed point numbers equal to FACT(n), where n=2..14. The last two numbers are used so that the final divide in some of the functions that use factorial will equal zero. Used to speed up the process. Maxe The maximum e^n can be for the current selection of FixedShift. This will be different for different setting of FixedShift, so it has to be calculated. I could have don all the calculations using the summation formulas given, but I wanted speed and decided on some precalculations. RandSeed Random number seed. RandMult Random number multiplier. RandIncr Random number increment. FIXED32.ASM: procedures FixedInit PROC FAR Called as the start up procedure in FIXED32.PAS. This function initializes the variables in FIXED32.ASM. InitRandom PROC FAR Seed:DWORD,Mult:DWORD,Incr:DWORD This initializes the random number generator in FIXED32.ASM. The random function starts with the same values every time, if you do not want this to happen you need to initialize with the timer, if you want it to be more random. FIXED32.ASM: functions FixedMult PROC FAR a:DWORD,b:DWORD RETURNS result:DWORD Multiplies a by b. (a*b) FixedMultNoCheck PROC FAR a:DWORD,b:DWORD RETURNS result:DWORD Multiplies a by b, with no over flow checking. (a*b) FixedDiv PROC FAR a:DWORD,b:DWORD RETURNS result:DWORD Divides a by b. (a/b) FixedDivNoCheck PROC FAR a:DWORD,b:DWORD RETURNS result:DWORD Divides a by b, with no over flow checking. (a/b) FixedDiv2 PROC FAR a:DWORD RETURNS result:DWORD Since Pascal only has the SHR operator and not the SAR operator, I put this in to divide by 2. This will work with negative number now. (a/2 or (a SAR 2)) FixedSqr PROC FAR a:DWORD RETURNS result:DWORD Squares a. (a^2) FixedSqrNoCheck PROC FAR a:DWORD RETURNS result:DWORD Squares a, with no over flow checking. (a^2) FixedSqrt PROC FAR a:DWORD RETURNS result:DWORD Takes the square root of the absolute value of a. (ABS(a)^(1/2)) FixedExp PROC FAR a:DWORD RETURNS result:DWORD Takes the exponential of a. (e^a) FixedLn PROC FAR a:DWORD RETURNS result:DWORD Takes the natural log of a. If a<0 then you will get MaxNeg as a return. (ln(a)) FixedCos PROC FAR a:DWORD RETURNS result:DWORD Takes the cosine of a. (sin(a)) FixedSin PROC FAR a:DWORD RETURNS result:DWORD Takes the sine of a. (cos(a)) FixedTan PROC FAR a:DWORD RETURNS result:DWORD Takes the tangent of a. (tan(a)) FixedArcTan PROC FAR a:DWORD RETURNS result:DWORD Takes the arctangent of a. Very slow when a is close to one, so the Pascal unit I do a little conversion routine if a is close to one. (arctan(a)) FixedRandom PROC FAR RETURNS result:DWORD Returns a fixed random number between 0 an 1. Random PROC FAR RETURNS result:DWORD Returns a random number along the full length of LongInt, positive and negative. COMPF32.PAS: type TFixedComplex Record of a,b of type TFixed. COMPF32.PAS: variables and constants Sqrt1_2 Constant equal (1/2)^(1/2). FixedSqrt1_2 Fixed point number equal to (1/2)^(1/2). COMPF32.PAS: procedures FixedComplexWriteLn(a:TFixedComplex); Displays to the screen the complex number in the form (a+bi) with a carriage return. FixedComplexWrite(a:TFixedComplex); Displays to the screen the complex number in the form (a+bi) without a carriage return. FixedComplexWriteLnConv(a:TFixedComplex); Displays to the screen the complex number in the form (a+bi) with a carriage return. It also converts a and b to real numbers before displaying. FixedComplexWriteConv(a:TFixedComplex); Displays to the screen the complex number in the form (a+bi) without a carriage return. It also converts a and b to real numbers before displaying. FixedComplexConj(a:TFixedComplex; var c:TFixedComplex); Returns the conjugate of a complex number where conj(a+bi)=a-bi. FixedComplexSign(a:TFixedComplex; var c:TFixedComplex); Returns a complex number with the same vector angle as a, but with a magnitude of one. FixedComplexNeg(a:TFixedComplex; var c:TFixedComplex); Negates a, where neg(a+bi)=-a-bi. FixedComplexPolToCar(a:TFixedComplex; var c:TFixedComplex); Converts polar coordinates to cartesian. Where the input is r+ theata*i to get a+bi. FixedComplexCarToPol(a:TFixedComplex; var c:TFixedComplex); Converts cartesian to polar coordinates. Where the input is a+bi to get r+theata*i. FixedComplexAdd(a,b:TFixedComplex; var c:TFixedComplex); Adds to complex numbers together. FixedComplexAddReal(a:TFixedComplex;b:TFixed; var c:TFixedComplex); Adds a complex number to a real number. FixedComplexAddImg(a:TFixedComplex;b:TFixed; var c:TFixedComplex); adds a complex number to an imaginary number. FixedComplexSub(a,b:TFixedComplex; var c:TFixedComplex); Subtracts two complex numbers. (a-b=c) FixedComplexSubReal(a:TFixedComplex;b:TFixed; var c:TFixedComplex); Subtracts a real number from a complex number. FixedComplexSubImg(a:TFixedComplex;b:TFixed; var c:TFixedComplex); Subtracts an imaginary number from a complex number. FixedComplexMult(a,b:TFixedComplex; var c:TFixedComplex); Multiplies two complex numbers. FixedComplexMultNoCheck(a,b:TFixedComplex; var c:TFixedComplex); Multiplies two complex numbers without over flow checking. FixedComplexMultReal(a:TFixedComplex;b:TFixed; var c:TFixedComplex); Multiplies a real number to a complex number. FixedComplexMultRealNoCheck(a:TFixedComplex;b:TFixed; var c:TFixedComplex); Multiplies a real number to a complex number without over flow checking. FixedComplexMultImg(a:TFixedComplex;b:TFixed; var c:TFixedComplex); Multiplies an imaginary number to a complex number. FixedComplexMultImgNoCheck(a:TFixedComplex;b:TFixed; var c:TFixedComplex); Multiplies an imaginary number to a complex number without over flow checking.. FixedComplexMulti(a:TFixedComplex; var c:TFixedComplex); Multiplies a complex number by i. Where (a+bi)*i=-b+ai. FixedComplexDiv(a,b:TFixedComplex; var c:TFixedComplex); Divides two complex numbers. (a/b) FixedComplexDivNoCheck(a,b:TFixedComplex; var c:TFixedComplex); Divides two complex numbers without over flow checking. FixedComplexDivReal(a:TFixedComplex;b:TFixed; var c:TFixedComplex); Divides a complex number by a real number. FixedComplexDivRealNoCheck(a:TFixedComplex;b:TFixed; var c:TFixedComplex); Divides a complex number by a real number without over flow checking. FixedComplexDivImg(a:TFixedComplex;b:TFixed; var c:TFixedComplex); Divides a complex number by an imaginary number. FixedComplexDivImgNoCheck(a:TFixedComplex;b:TFixed; var c:TFixedComplex); Divides a complex number by an imaginary number without over flow checking.. FixedComplexInv(a:TFixedComplex; var c:TFixedComplex); Inverts a complex number. (1/a) FixedComplexInvNoCheck(a:TFixedComplex; var c:TFixedComplex); Inverts a complex number without over flow checking. FixedComplexSqr(a:TFixedComplex; var c:TFixedComplex); Squares a complex number. FixedComplexSqrNoCheck(a:TFixedComplex; var c:TFixedComplex); Squares a complex number without over flow checking. FixedComplexSqrt(a:TFixedComplex; var c:TFixedComplex); Takes the square root of a complex number. FixedComplexSqrtReal(a:TFixed; var c:TFixedComplex); Takes the square root of a real number. FixedComplexSqrtImg(a:TFixed; var c:TFixedComplex); Takes the square root of an imaginary number. FixedComplexExp(a:TFixedComplex; var c:TFixedComplex); Takes the exponential of a complex number. FixedComplexExpReal(a:TFixed; var c:TFixedComplex); Takes the exponential of a real number. FixedComplexExpImg(a:TFixed; var c:TFixedComplex); Takes the exponential of an imaginary number. FixedComplexLn(a:TFixedComplex; var c:TFixedComplex); Takes the natural logarithm of a complex number. FixedComplexLnReal(a:TFixed; var c:TFixedComplex); Takes the natural logarithm of a real number. FixedComplexLnImg(a:TFixed; var c:TFixedComplex); Takes the natural logarithm of an imaginary number. FixedComplexSin(a:TFixedComplex; var c:TFixedComplex); Takes the sine of a complex number. FixedComplexSinReal(a:TFixed; var c:TFixedComplex); Takes the sine of a real number. FixedComplexSinImg(a:TFixed; var c:TFixedComplex); Takes the sine of and imaginary number. FixedComplexASin(a:TFixedComplex; var c:TFixedComplex); Takes the arcsine of a complex number. FixedComplexASinReal(a:TFixed; var c:TFixedComplex); Takes the arcsine of a real number. FixedComplexASinImg(a:TFixed; var c:TFixedComplex); Takes the arcsine of an imaginary number. FixedComplexCos(a:TFixedComplex; var c:TFixedComplex); Takes the cosine of a complex number. FixedComplexCosReal(a:TFixed; var c:TFixedComplex); Takes the cosine of a real number. FixedComplexCosImg(a:TFixed; var c:TFixedComplex); Takes the cosine of an imaginary number. FixedComplexACos(a:TFixedComplex; var c:TFixedComplex); Takes the arccosine of a complex number. FixedComplexACosReal(a:TFixed; var c:TFixedComplex); Takes the arccosine of a real number. FixedComplexACosImg(a:TFixed; var c:TFixedComplex); Takes the arccosine of an imaginary number. FixedComplexTan(a:TFixedComplex; var c:TFixedComplex); Takes the tangent of a complex number. FixedComplexTanReal(a:TFixed; var c:TFixedComplex); Takes the tangent of a real number. FixedComplexTanImg(a:TFixed; var c:TFixedComplex); Takes the tangent of an imaginary number. FixedComplexATan(a:TFixedComplex; var c:TFixedComplex); Takes the arctangent of a complex number. FixedComplexATanHP(a:TFixedComplex; var c:TFixedComplex); Takes the arctangent of a complex number. You are probably wondering why I have two different versions of arctangent. My HP 28S uses a different equation and comes up with a different answer for certain inputs. My equation and the my HP's are mathematically equal, but the answers are different. How could that be you ask, well check inside the code and there is an explanation. FixedComplexATanReal(a:TFixed; var c:TFixedComplex); Takes the arctangent of a real number. FixedComplexATanImg(a:TFixed; var c:TFixedComplex); Takes the arctangent of an imaginary number. FixedComplexPower(a,b:TFixedComplex; var c:TFixedComplex); Takes a to the power of b. (a^b) FixedComplexPowerReal(a:TFixedComplex;b:TFixed; var c:TFixedComplex); Takes a complex number to the power of a real number. FixedComplexPowerImg(a:TFixedComplex;b:TFixed; var c:TFixedComplex); Takes a complex number to the power of an imaginary number. COMPF32.PAS: functions FixedComplexReal(a:TFixedComplex):TFixed; Return the real part of a complex number. FixedComplexImg(a:TFixedComplex):TFixed; Returns the imaginary part of a complex number. FixedComplexAbs(a:TFixedComplex):TFixed; Return the angle of the vector. FixedComplexArg(a:TFixedComplex):TFixed; Return the magnitude of the vector. As you will notice I added several extra functions in the COMPF32.PAS that were not needed. I plan in the future to overload the operators in C++. Actually I started this project in C++ with that intention in mind, but I ran into problems. When I defined TFixed as an unsigned long, C++ would overload the unsigned long also. So I tried defining TFixed as a struc, but it did not work. How do I get around this? Maybe I'll just check with Borland. Comments, Improvements, Donations, or those of you who want to make money with this library can reach me at: Eric Nixon 1590 Hawk Circle Apt. C Troy OH, 45373 Remember vegetables have rights and should not be stepped on, picked, plucked, diced, or eaten. GOD gave use cows to eat not plants. VRA.....