The Math Library allows high precision computations not available through standard integer operations. Floating Point (FP) numbers use decimal representation to express fractional quantities. This format also allows scientific notation to represent very large and very small numbers.
To enable floating point operations, an application must include a GEOS Math Library. There are two Math Libraries: math.h and math.goh . The file math.h includes what you need to get floating point numbers to work within your application. The object file, math.goh (which includes math.h ), includes the float-format controller, an object that allows the user to control how floating point numbers are formatted within an application.
In most cases, the mere inclusion of the Math Library will eliminate the need to directly call Math Library routines. (The C compiler will parse the mathematical expressions into appropriate floating point routines.) You may, however, wish to call some of these routines directly. Developers using Object Assembly must use this latter approach. C developers may also want to directly manipulate the FP stack for involved computations that they would rather not leave to the compiler. This approach is discussed in the latter half of this chapter.
Use of math.goh is optional, depending on the purpose of the application. A spreadsheet application, for example, might want to allow the user to format FP numbers. Other applications might not want to include this file.
1 Basic Math Functions
1.1 Algebraic Functions
1.2 Transcendental Functions
1.3 Random Number Generation
2 Conversions to Other Types
3 Float Formats
3.1 System-defined Formats
3.2 User-defined Formats
4 Direct FP Operations
4.1 Floating Point Numbers
4.2 The Floating Point Stack
4.3 Floating Point Math Routines
The Math Library includes many routines and structures that make the manipulation of FP numbers possible. Most of these are transparent to a C programmer.
C includes the following floating point types:
float
,
double
, and
long
double
. A floating point number in GEOS is of type
FloatNum
, which uses the IEEE 80 bit standard (equivalent to a long double) to represent floating point values.
FloatNum
has overloaded the float, double, long double, and all basic math functions.
A
FloatNum
consists of a one bit sign, a 15 bit exponent, and a 64 bit mantissa. The maximum exponent allowed is 7FFEh. An exponent of 7FFFh (FP_NAN) signals an underflow or overflow. You may check the value of this exponent using the macro
FLOAT_EXPONENT to extract the exponent of a
FloatNum
.
Code Display D-1 Extracting an Exponent
FloatNum myNum;
if (FLOAT_EXPONENT(&myNum) == FP_NAN)
{
return(ERROR);
}
If you use any floats, doubles, or long doubles in your application, GEOS will convert these types into
FloatNum
values automatically and call the appropriate GEOS functions to manipulate the numbers. As this is the case, it is usually easier to declare any floating point variables in your application as
FloatNum
types (or long doubles). This cuts down on conversion time.
The Math Library provides all the familiar mathematical functions to manipulate these GEOS FP numbers (addition, multiplication, computation of logarithms, etc.). In C, most of these functions will automatically be called when their corresponding C operation involving FP numbers take place. (For example, when using the `+' binary operator to add two FP numbers, the Math Library will use the corresponding
FloatAdd()
routine.)
You may, under special circumstances, wish to use these math routines directly. If you do so, you will need to manipulate the floating point stack manually, pushing numbers on the stack and making sure numbers are in the proper stack location to perform each operation. In most cases, the FP math routines operate on numbers already in place on the FP stack; they take and return no arguments of their own.
Two functions you will need to use if you take this approach are
FloatPushNumber()
and
FloatPopNumber()
.
FloatPushNumber()
takes the address of a variable (of type
FloatNum
) to push onto the FP stack;
FloatPopNumber()
takes the address of a buffer to place an FP number popped off the FP stack. Other routines (for example,
FloatAdd()
) can then be called to operate on the FP stack. (See Adding Two FP Numbers
for an example using this approach.)
There are many other routines that perform stack manipulation by shifting locations of FP numbers on the stack (
FloatRoll()
,
FloatDrop()
, etc.). These routines are covered in detail in the latter part of this chapter because they are seldom needed in a C applications.
Algebraic routines perform algebraic operations on FP numbers. The Math Library provides all of the basic algebraic routines that operate on GEOS FP numbers (addition, subtraction, etc.).
If you wish to call these routines directly rather than rely on the C operations, you may manipulate the floating point stack directly. To add two numbers using
FloatAdd()
for example, you would use
FloatPushNumber()
twice to push the two values to add onto the FP stack, and then call
FloatAdd()
to operate on the FP stack. (See Adding Two FP Numbers
.)
Code Display D-2 Adding Two FP Numbers
/* * The following two methods each add two FP numbers and return the result. The * first method is familiar C code. The second example uses the floating point * routines from math.h directly. Note that the C code will be assembled into code * that uses FloatAdd() also, but that this is transparent to the code. */
@method MyProcessClass, MSG_SUM_FLOATS {
long double number1, number2, number3;
number1 = 1.0;
number2 = 2.0;
number3 = number1 + number2;
return(number3);
}
@method MyProcessClass, MSG_SUM_FLOATS_MANUALLY {
long double number1, number2, number3;
number1 = 1.0;
number2 = 2.0;
FloatPushNumber(&number1); /* Push number1 onto the FP stack. */
FloatPushNumber(&number2); /* Push number2 onto the FP stack. */
FloatAdd(); /* Add the top two numbers on the FP stack. The
* result will be placed on top of the FP stack.*/
FloatPopNumber(&number3); /* Pop the result into the number3 variable. */
return(number3);
}
Several functions have no equivalent C operation. Consult Direct FP Operations for more details on using these functions.
Note that although direct comparisons between FP numbers are not allowed, you can use the
FloatLt0(), FloatGt0(),
and
FloatEq0()
functions to compare whether the the addition of two FP numbers is gretaer than, less than, or equal to zero.
The Math Library provides an array of transcendental routines that operate on GEOS FP numbers. A transcendental function is one which cannot be derived through algebraic means. Examples of transcendental functions include the trigonometric functions (sine, cosine, etc.) and the logarithmic functions (log, natural log, etc.).
The table above lists the function names and the operations they perform. Typical functions in C are listed alongside. (The basic language of C itself includes no such transcendental functions but almost all compilers include a C math library that does.) As can be seen in the table, several functions have no equivalent C operation. Consult Float Formats for more details on using these functions.
FloatRandomize(), FloatRandom(), FloatRandomN()
The Math Library also provides routines to create random numbers. Using any of these routines requires that you manually push and pop numbers on the FP stack.
FloatRandomize()
primes the random number generator, in preparation for a call to
FloatRandom()
or
FloatRandomN()
. If
FloatRandomize()
is passed the flag RGIF_USE_SEED, the routine must also pass a developer supplied seed.
FloatRandom()
returns a random value between 0 (inclusive) and 1 (exclusive). The number is placed on top of the FP stack. To assign that value to a variable, use
FloatPopNumber()
.
FloatRandomN()
returns a random value between 0 (inclusive) and N (exclusive), where N is an integer. The integer value must be on top of the FP stack. The returned integer is pushed onto the FP stack.To assign that value to a variable, use
FloatPopNumber()
.
Code Display D-3 Creating a Random Number
/* * This method takes a passed seed (passedSeed) and returns a random integer * between 0 and 100. */
@method MyProcessClass, MSG_GET_RANDOM_FLOAT {
long double randomX;
/* Prime the random number generator. */
FloatRandomize(RGIF_USE_SEED, passedSeed);
randomX = 100;
FloatPushNumber(&randomX);
FloatRandomN();
FloatPopNumber(&randomX);
return(randomX);
}
In many cases, FP numbers will need to be converted to different types for use in different parts of an application. For example, floating point numbers may be involved in a complex function that returns an integer. FP numbers may also need to appear to the user as ASCII text.
There are several routines which convert GEOS FP numbers into other float formats, compatible with the C types
float
and
double
. Typically in C, this conversion is accomplished by casting the FP numbers into the other type. It is therefore done automatically for you.
If you are working in Assembly or you wish to directly pass floats or doubles to C stubs, consult Direct FP Operations .
FloatAsciiToFloat()
FloatAsciiToFloat()
converts a number represented in an ASCII text format into a FP number. The routine recognizes two flags:
The routine must also be passed a pointer to the string to convert, the number of characters in the string to convert (starting at the address) and the buffer to store the FP number if passing FAF_STORE_NUMBER.
FloatFloatToAscii(), FloatFloatToAscii_StdFormat(), FloatFloatIEEE64ToAscii_StdFormat()
FloatFloatToAscii()
converts an FP number into ASCII text format. The routine must be passed a stack frame, which may be set up by declaring a local variable of type
FFA_stackFrame
and moving data into the appropriate fields. You should also pass the routine a pointer to a buffer to store the resultant string. This buffer must be declared with a length of either:
The
FFA_stackFrame
is a union of two structures:
FloatFloatAsciiData
or
FloatFloatToDateTimeData
. You will want to use the
FloatFloatToAsciiData
structure in most cases;
FloatFloatToAsciiDateTimeData
is used to format a FP number (representing a date and time) into a date-time format passed in the structure. The routine checks a bit in the structure to see which structure is being passed.
The
FloatFloatToAsciiData
structure is used most often in formatting FP numbers into ASCII. The structure is rather large and cumbersome to set up. You may wish to use the routine
FloatFloatToAscii_StdFormat()
which sets up many of these entries automatically. ( FloatFloatToAsciiData Structure
lists the entries of the
FloatFloatToAsciiData
structure.)
Code Display D-4 FloatFloatToAsciiData Structure
typedef struct {
/* * FFA_params stores the entries that the caller must set up. */ FloatFloatToAsciiParams FFA_params;
/* * These entries store information returned by FloatFloatToAscii() that may be * examined. */ word FFA_startNumber; word FFA_decimalPoint; word FFA_endNumber; word FFA_numChars; word FFA_startExponent;
/* * The rest of the entries are for internal use only. */ word FFA_bufSize; word FFA_saveDI; word FFA_numSign; byte FFA_startSigCount; byte FFA_sigCount; byte FFA_noMoreSigInfo; byte FFA_startDecCount; byte FFA_decCount; word FFA_decExponent; word FFA_curExponent; byte FFA_useCommas; byte FFA_charsToComma; char FFA_commaChar; char FFA_decimalChar; } FloatFloatToAsciiData;
FFA_
params
is a structure that stores the following entries of its own:
decimalOffset
of -6 shifts the decimal point six places to the left; to display in terms of tenths would require a
decimalOffset
of 1.
decimalLimit
of 2 would print out 123.456789 as 123.46.
The
formatFlags
record is a record of type
FloatFloatToAsciiFormatFlags
and defines the format of the ASCII output. Set the appropriate flags to get the desired output.
FloatFloatToAscii()
if this flag is set.
The rest of the entries in
FloatFloatToAsciiData
store information filled in by
FloatFloatToAscii()
. These entries are described below:
startNumber
stores the offset to the start of numeric characters in the ASCII buffer.
decimalPoint
stores the offset to the decimal point character or zero if no decimal point is present.
endNumber
stores the offset to the end of the numeric characters in the ASCII buffer.
numChars
stores the total number of characters in the ASCII buffer (excluding the null terminator). This entry is set to zero if an error is encountered.
startExponent
stores the offset to the "E" character in the ASCII buffer or zero if no exponent is present. Applications can check this to see if the number was expressed in scientific notation using the `E' format.
FFA_stackFrame
may contain
FloatFloatToAsciiDateTimeData
if
FloatFloatToAscii()
is being used to convert a FP number into a date-time format. In that case
FFA_stackFrame
contains the structure
FloatFloatToDateTimeData
instead of
FloatFloatToAsciiData
. (
FFA_stackFrame
is a union.)
FloatFloatToDateTimeData
contains one entry, FFA_
dateTimeParams
. This structure contains several flags which specify how the date-time should be formatted and a number of entries which break down the date-time into its respective parts (year, month, day etc.) If none of these entries are filled in, the date-time is taken from the top of the FP stack.
Date-times are represented by FP numbers in GEOS. The integer portion represents dates as integers counted from Jan 1, 1900, which is designated as 1. The highest date allowed is 73049 (December 31, 2099).
The fractional portion represents a fraction of the day between midnight (0.000000) and 11:59:59 p.m. (0.999988). This fractional value is derived from the hour, minute and second of the day.
Code Display D-5 DateTime Parameters
typedef struct {
FloatFloatToDateTimeFlags FFA_dateTimeFlags;
word FFA_year;
byte FFA_month;
byte FFA_day;
byte FFA_weekday;
byte FFA_hours;
byte FFA_minutes;
byte FFA_seconds;
} FloatFloatToDateTimeParams;
/* * FloatFloatToDateTimeFlags record */
typedef WordFlags FloatFloatToDateTimeFlags; #define FFDT_DATE_TIME_OP 0x8000 #define FFDT_FROM_ADDR 0x4000 #define FFDT_FORMAT 0x3fff
The flag FFDT_DATE_TIME_OP is set to notify the
FloatFloatToAscii()
routine that this operation is a date-time format, not a normal float to ASCII conversion. This flag must be set if you want to convert the FP number into a date-time format using
FloatFloatToAscii()
.
FFDT_FROM_ADDR is set if the date-time FP number should not be taken from the FP stack (or passed directly in the
FloatFloatToDateTimeParams
structure) but should instead be taken from the address passed in
FloatFloatToAscii()
.
FFDT_FORMAT stores the
DateTimeFormat
that the routine will use to format the number into a date-time string.
If the date-time is directly passed in, and not taken from an FP date-time number either at a passed address or the top of an FP stack,
FloatFloatToAscii()
looks at the other passed parameters.
FFA_
year
specifies the year. The value must be between 1900 and 2099. This is not a one-based year, as it is when presented as a date-time number.
FFA_
month
is the month of the year, a value between 1 and 12.
FFA_
day
is the day of a month, a value between 1 and 31.
FFA_
hour
specifies the hour of the day, a value between 0 and 23. Zero specifies midnight.
FFA_
minutes
specifies the minute of the hour, a value between 0 and 59.
FFA_
seconds
specifies the second of the minute, a value between 0 and 59.
FloatFloatToAscii_StdFormat()
uses a pre-set stack frame, eliminating the need to set up the variables of the
FloatFloatToAsciiData
structure manually. The only flags recognized are FFAF_FROM_ADDR, FFAF_SCIENTIFIC, FFAF_PERCENT, FFAF_USE_COMMAS, and FFAF_NO_TRAIL_ZEROS. The developer must pass the number of total digits and the number of decimal digits desired. If the flag FFAF_FROM_ADDR is used, a pointer to a FP number (not on the FP stack) must also be passed.
The standard format sets the following elements of the stack frame to zero:
decimalOffset, header, trailer, postNegative, prePositive
, and
postPositive
. The structure element
preNegative
is set to the minus sign ("-").
FloatFloatIEEE64ToAscii_StdFormat()
performs the same operation as
FloatFloatToAscii_StdFormat()
except that the FP number is passed (in 64 bit format) and is not taken from the stack. The entire FP number (not just a pointer to it) must be passed. All criteria for
FloatFloatToAscii_StdFormat()
applies to this routine, except that the flag FFAF_FROM_ADDR is not used.
FloatGetDateNumber(), FloatDateNumberGetYear(), FloatDateNumberGetMonthAndDay(), FloatDateNumberGetWeekday(), FloatGetTimeNumber(), FloatTimeNumberGetHour(), FloatTimeNumberGetMinutes(), FloatTimeNumberGetSeconds()
FloatGetDateNumber()
, when passed the month, day, and year, converts the data into an FP "date number" representation. This format represents dates as integers counted from Jan 1, 1900, which is designated as 1. The highest date allowed is 73050 (December 31, 2099).
FloatDateNumberGetYear(), FloatDateNumberGetMonthAndDay()
and
FloatDateNumberGetWeekday()
all return the appropriate data, either the year, month and day, or weekday, given an FP "date number" as defined above. All data are returned as integers, not as FP numbers, and the original FP "date number" is popped off of the stack. Years are returned as integers between 1900 and 2099. Month and Days are returned as integers between 1 and 12, and 1 and 31, respectively. Weekdays are returned as integers between 1 and 7, where 1 is Sunday, 2 is Monday, etc.
FloatGetTimeNumber()
when passed hours, minutes, and seconds returns an FP decimal representation between midnight (0.000000) and 11:59:59 p.m. (0.999988).
FloatTimeNumberGetHour(), FloatTimeNumberGetMinutes()
and
FloatTimeNumberGetSeconds()
return the appropriate data given an FP "time number" as defined above. The original FP "time number" is popped off of the stack.
Note that both "date numbers" and "time numbers" can be added to specify a specific point in time. For example, 73050.999988 would specify December 31, 2099, 11:59:59. Since these formats are in FP format, they can be operated on with all standard functions in the FP library.
FloatGetDaysInMonth(), FloatGetNumDigitsInIntegerPart(), FloatFormatNumber()
FloatGetDaysInMonth()
returns the total number of days in a specific month, for a specific year. The routine must be passed the appropriate month and year.
FloatGetNumDigitsInIntegerPart()
returns the number of digits in the integer portion of an FP number. Numbers between zero and one will return one as the number of digits.
FloatStringGetDateNumber(), FloatStringGetTimeNumber()
FP numbers can be displayed in many ways. For example, as we have seen, an FP number may actually represent a date-time. When we display the FP number 366.0000, we may want to show it (in text) as "Jan 1, 1901." The Math Library has a number of system-defined formats for your use. You may also allow users to define their own formats with the Float Format controller.
The underlying structures and routines to use and create float format options are explained in the next section. In most cases, however, the simple inclusion of a Float Format controller (of
FloatFormatClass
) provides all of the UI and functionality to create and apply formats to FP numbers within text objects.
A system-defined FP format is stored within a
FormatParams
structure. This structure defines whether the FP number is a number to be converted into numerical text or a date-time. These format parameters are stored within arrays managed by the format control code.
Code Display D-6 System-defined Float Formats
/* * System-defined float formats are stored in an array that is maintained and * accessed by the float controller code. Each element is made up of a * FormatParams structure. */
typedef struct {
/*
* The FloatFloatToAsciiParams_Union stores either a
* FloatFloatToAsciiParams structure if the number is a `pure' FP number,
* or a FloatFloatToDateTimeParams structure if the number is a date-time.
* In this way, it is essentially the same as the FFA_stackFrame discussed
* earlier.
*/
FloatFloatToAsciiParams_Union FP_params;
/* * FP_formatName stores the name of this formatting option that will be * displayed in the float controller's dynamic list. This name is loaded * from the optr given in FP_nameHan and FP_nameOff.(The table where these * strings are kept is within a localizable resource and therefore will * have different text under different country setups.) */ char FP_formatName[FORMAT_NAME_LENGTH+1]; word FP_nameHan; /* MemHandle */ word FP_nameOff; /* ChunkHandle */
/* * FP_listEntryNum stores the zero-based position of this FormatParams * entry within the table. */ word FP_listEntryNum;
/* * FP_signature is an internal field used for error-checking. */ word FP_signature; } FormatParams;
An application will never need to access this table of formats directly. GEOS contains several routines (in math.goh ) that can access this table and add, delete and modify table entries. Usually, it is easiest to include a Float Format controller in your application if you intend to allow the user to change float formats with these routines.
There are many system-defined float formatting options. These formats are identified by
FormatIdType
enumerations. Each type corresponds to a
FormatParams
structure.
Each
FormatIdType
enumeration is a direct offset into the float format lookup table. To distinguish between system-defined and user-defined formats, the high bit of a
FormatIdType
is set to indicate that the format is system-defined. Thus, 8000h refers to the first system-defined format, 8000h + (size(
FormatParams
)) refers to the second system-defined format, etc.
The format strings themselves are stored within a localizable resource, so that they may appear in a manner relevant to the particular country involved. For example, an FP number of 12.0 using the
FormatIdType
FORMAT_ID_CURRENCY might appear in the U.S. as $12.00, but will appear as £12.00 in Great Britain.
Code Display D-7 Float Format IDs
typedef enum {
FORMAT_ID_GENERAL = 0x8000,
FORMAT_ID_FIXED = 0x8061,
FORMAT_ID_FIXED_WITH_COMMAS = 0x80c2,
FORMAT_ID_FIXED_INTEGER = 0x8123,
FORMAT_ID_CURRENCY = 0x8184,
FORMAT_ID_CURRENCY_WITH_COMMAS = 0x81e5,
FORMAT_ID_CURRENCY_INTEGER = 0x8246,
FORMAT_ID_PERCENTAGE = 0x82a7,
FORMAT_ID_PERCENTAGE_INTEGER = 0x8308,
FORMAT_ID_THOUSANDS = 0x8369,
FORMAT_ID_MILLIONS = 0x83ca,
FORMAT_ID_SCIENTIFIC = 0x842b,
FORMAT_ID_DATE_LONG = 0x848c, FORMAT_ID_DATE_LONG_CONDENSED = 0x84ed, FORMAT_ID_DATE_LONG_NO_WKDAY = 0x854e, FORMAT_ID_DATE_LONG_NO_WKDAY_CONDENSED = 0x85af, FORMAT_ID_DATE_SHORT = 0x8610, FORMAT_ID_DATE_SHORT_ZERO_PADDED = 0x8671, FORMAT_ID_DATE_LONG_MD = 0x86d2, FORMAT_ID_DATE_LONG_MD_NO_WKDAY = 0x8733, FORMAT_ID_DATE_SHORT_MD = 0x8794, FORMAT_ID_DATE_LONG_MY = 0x87f5, FORMAT_ID_DATE_SHORT_MY = 0x8856, FORMAT_ID_DATE_YEAR = 0x88b7, FORMAT_ID_DATE_MONTH = 0x8918, FORMAT_ID_DATE_DAY = 0x8979, FORMAT_ID_DATE_WEEKDAY = 0x89da, FORMAT_ID_TIME_HMS = 0x8a3b, FORMAT_ID_TIME_HM = 0x8a9c, FORMAT_ID_TIME_H = 0x8afd, FORMAT_ID_TIME_MS = 0x8b5e, FORMAT_ID_TIME_HMS_24HR = 0x8bbf, FORMAT_ID_TIME_HM_24HR = 0x8c20,
FORMAT_ID_INDETERMINATE = 0xffff } FormatIdType;
Users may also design their own float formatting options. These user-defined formats are stored in
FormatEntry
structures. The array of these user-defined formats is kept separate from the system-defined formats. (user-defined formats are stored within their own VM block).
User-defined
FormatIdType
enums do not have the high bit (8000h) set in order to distinguish them from system-defined
FormatIdType
enums.
Code Display D-8 User-defined Formats
/* * User-defined formats are stored in a VM block that is created and maintained by * the float controller code. As each new format is added, space is made for a * FormatEntry structure. As formats are deleted, their entry's FE_used field is * set to zero to indicate that the entry is free for new formats to overwrite. */
typedef struct {
/* * Each FormatEntry contains a corresponding FormatParams structure. */ FormatParams FE_params;
/* * FE_listEntryNumber is the zero-based position of the format counting * both previous user-defined formats and system-defined formats. For * example, if there are 10 system-defined formats, the first user-defined * format will have a FE_listEntryNumber of 10 (because positions are * zero-based). */ word FE_listEntryNumber;
/* * FE_used, if non-zero, indicates that this entry within the user-defined * list is currently in use. If FE_used is zero, then this entry position * may be used to add a new user-defined entry without increasing the size * of the VM block. */ byte FE_used;
/* * FE_sig is an error-checking field used internally. */ word FE_sig; } FormatEntry;
The Math Library allows your application to use floating point (FP) numbers. C Developers will find little reason to make direct calls to functions in the Math Library as most of this functionality is taken care of for you in the C environment. (An exception is conversions from FP numbers to ASCII text characters and use of special date-time routines.) Assembly developers, however, will find direct use of the functions and structures in the Math Library essential.
The latter half of this chapter (from this point on) is provided for Assembly developers who need more complete information about the intricacies of the Math Library. Much of this information may also be useful for C developers who wish to make direct calls to the functions in this library for optimization purposes.
The Math Library defines floating point numbers by using binary point representation. In this format the bit in the zeros place is multiplied by 20, the bit in the minus-ones place is multiplied by 2-1, etc. The exponent is in base 2. For example, the Binary Point representation for 5 is:.
Floating Point Numbers in GEOS follow the IEEE 754 standard used by Intel. In this format a floating point number is represented in an 80 bit (5 word) format. (In C, this type is known as a
long
double
.) This format specifies that the 80 bits contain:
The 15 bit exponent is biased by 3FFFh, so that an exponent of 1 would be represented by 4000h, and an exponent of -1 would be represented by 3FFEh. This produces a hexadecimal range for the exponent of 4000h, or a decimal range of 4932.
Infinity is represented by 7FFFh in the exponent and 1 followed by 63 zeros in the mantissa. Therefore, all exponent bits are set to 1. (Negative infinity is the same except that the sign bit is also set to 1).
Zero is a special case in which all 80 bits-- sign, exponent, and mantissa--are set to zero.
The mantissa is normalized so that the most significant bit is always 1 (except when the FP number is zero). The binary point follows this 1 bit. This produces a precision of approximately 19 decimal places, which is adequate for most needs. This most significant bit is not "assumed away"; it is always present in the mantissa.
Note that in most uses, the use of binary point representation is transparent to the application.
Floating Point (FP) numbers are placed and manipulated on an FP stack. Numbers can be rearranged, operated on, and removed from this stack through pushes, pops, and the use of special routines.
FloatInit(), FloatExit()
Before performing any floating point operations, a thread needs to call
FloatInit()
to create and initialize an FP stack. Each thread using floats must have its own unique FP stack. This call to
FloatInit()
is automatically performed by any application that includes the Math Library.
FloatInit()
creates a swapable block of memory for the thread, initializes various stack pointers, and stores the handle for the block in the thread's data structure.
FloatInit()
must be passed the size of the stack to create (in bytes) and the type of stack (
FloatStackType
) to create.
The default FP stack holds 25 FP elements (250 bytes). An FP stack must be able to hold at least 5 FP elements.
The default floating point stack is FLOAT_STACK_GROW which instructs the system to increase the size of the stack whenever its bounds are reached. This is done automatically.
Other
FloatStackType
types are FLOAT_STACK_WRAP, which drops the FP numbers at the low end of the stack (effectively wrapping over that end) and FLOAT_STACK_ERROR which signals an error when the stack limit has been reached.
FloatExit()
detaches the floating point stack for the current thread and frees its memory.
FloatExit()
only frees the FP stack associated with the current thread; other FP stacks in other threads remain unaffected. As is the case with
FloatInit()
, the call to
FloatExit()
is automatically performed by any application that includes the Math Library.
If
FloatInit()
is called twice before calling
FloatExit()
, the data on the original floating point stack will be lost.
FloatPushNumber(), FloatPopNumber(), FloatDepth()
FloatPushNumber()
pushes an FP number onto the top of the FP stack for the current thread from a passed buffer. The number must be already set up in 80 bit, FP format. The routine must be passed the pointer to the buffer storing the number.
Similarly,
FloatPopNumber()
pops an FP number from the top of the FP stack for the current thread into a passed buffer.
FloatDepth()
returns the number of FP numbers currently in place on the stack.
Note: For clarity in diagrams within this chapter, stack locations will be numbered in order from the top position of the stack, S1 being first, S2 being second, etc. Variables will be numbered in the order they are pushed onto the stack, so that if X1, X2, and X3 are pushed onto the stack, the format illustrated below will result.
FloatRoll(), FloatRollDown(), FloatRot(), FloatSwap()
Besides basic pushing and popping, the Float Library also includes many other routines which manage FP numbers on the stack. Unless otherwise specified, an operation that pushes, pops, or extracts an FP number on the stack affects all other FP numbers below the position of the operation by shifting their location in the stack either up or down, in standard stack fashion.
FloatRoll()
pushes a selected FP number (S
X
) onto the top of the stack (S1), removing it from location SX in the process.
FloatRoll()
passed with a value of 3 would move the FP number in S3 onto the top of the stack, pushing the stack in the process. All FP numbers below the extracted number remain unaffected by this routine.
FloatRollDown()
performs the inverse operation of
FloatRoll()
. popping the top stack value (S1) into the specified location on the stack (S
X
).
FloatRollDown()
passed with a value of 3 would move the FP number in S1 into location S3, shifting the stack in the process.
Both of these routines must be passed a stack location to move to or from.
FloatRot()
rotates the top three numbers on the stack, placing S3 onto the top of the stack. This is equivalent to a
FloatRoll()
passed with a value of 3.
FloatSwap()
exchanges S1 and S2.
Repetitious applications of these routines will return the stack to its former state.
FloatPick(), FloatOver(), FloatDrop(), FloatDup()
FloatPick()
copies the contents of S
X
and pushes that value onto the FP stack. The entire stack is pushed in the process.
FloatPick()
passed with a value of 3 would copy the contents of S3 onto the FP stack.
FloatOver()
copies S2 to the top of the stack, equivalent to a
FloatPick()
passed with a value of 2.
FloatDrop()
drops (pops) the top number (S1) off the FP stack. This routine is different than
FloatPopNumber()
because the routine does not place the popped number into a memory address, and is therefore much faster.
FloatDup()
duplicates the value at S1, pushing it onto the top of the stack. The stack is pushed in the process.
FloatComp(), FloatCompESDI(), FloatCompAndDrop()
These routines essentially perform the same operation as the Assembly command
cmp
.
FloatComp()
performs a compare of the top two FP numbers (S1 and S2) and sets the appropriate flags in the flags register. The two FP numbers remain on the stack.
FloatCompESDI()
compares the contents of
es:[di]
with the value in S1 (and the FP number in S1 remains on the stack).
FloatCompAndDrop()
performs a compare of S1 and S2 and drops both from the FP stack.
FloatGetStackPointer(), FloatSetStackPointer()
FloatGetStackPointer()
returns the current stack pointer value of the FP stack.
FloatSetStackPointer()
sets the stack pointer to a previous position. This routine must be passed a value that is greater than or equal to the current value of the stack pointer. (I.e. you must be throwing something, or nothing, away.)
These routines may be useful before the execution of involved routines that may push many numbers onto the stack. If an unrecoverable error is encountered, the programmer need not pop the intermediate values off the stack to return to the previous stack configuration. Only the stack pointer is saved; the state of the stack is not. If any numbers below the stack pointer are popped or altered,
FloatSetStackPointer()
will not recover the previous state of the stack.
The Float Library provides a number of routines to modify data on the FP stack. These routines can be categorized in several major groups:
Float0(), FloatPoint5(), Float1(), FloatMinusPoint5(), FloatMinus1(),Float2(), Float5(), Float10(), Float3600(), Float16384(), Float86400()
FloatPi(), FloatPiDiv2(),FloatLg10(), FloatLn2(), FloatLn10(), FloatSqrt2()
The Constant Routines provide a means of quickly obtaining an often used number or an often used operation with a constant operand. Each of these functions pushes the constant FP value onto the top of the FP stack. (For example,
FloatMinusPoint5()
pushes -.5 onto the FP stack.)
FloatPi()
,
FloatPiDiv2()
,
FloatLg10()
,
FloatLn2()
,
FloatLn10()
, and
FloatSqrt2()
each push the specified transcendental constant onto the FP stack: p,p/2, the log of 10, the natural log of 2, the natural log of 10,and the square root of 2, respectively. (See also Transcendental Routines.)
FloatMultiply2(), FloatMultiply10(), FloatDivide2(), FloatDivide10(), Float10ToTheX()
FloatMultiply2(), FloatMultiply10(), FloatDivide2(), FloatDivide10()
perform the specified operations on the contents of S1, either multiplying or dividing the contents of S1 by 2 or 10, and push the result onto the FP stack. The original value in S1 is popped off of the stack.
Float10ToTheX()
pushes 10 to a passed exponent onto the FP stack.
FloatAbs(), FloatAdd(), FloatSub(), FloatDivide(), FloatMultiply(), FloatDIV(), FloatMod(),FloatFactorial(), FloatNegate(), FloatInverse()
The Math Routines perform various operations on numbers already placed on the FP stack. All of the following routines pop the operated numbers off of the stack and push the result onto S1.
FloatAdd()
adds the contents of S1 and S2, and pushes the result onto the top of the FP stack. The original values are popped off the stack.
FloatSub()
subtracts
the contents of S1 from the contents of S2, and pushes the result onto the top of the FP stack. The original values are popped off the stack.
Note that, in general, numbers will be pushed onto the stack in the order they would normally be operated on. For example, to obtain X1-X2, the programmer would first push X1, then X2, and call
FloatSub
, as in the following diagram:
since X1 would now be in location S2, while X2 is in location S1.
FloatMultiply()
multiplies the contents of S1 and S2 and pushes the result onto the top of the FP stack. The original values are popped off the stack.
FloatDivide()
divides the contents of S2 by the contents of S1 and pushes the result onto the top of the FP stack. The original values are popped off the stack.
FloatDIV()
performs a
FloatDivide()
, truncating the fractional portion of the number and returning only the integer result. The original values are popped off the stack.
FloatMod()
pushes S2 mod S1 (the remainder of
FloatDivide()
) onto the top of the FP stack. The original values are popped off the stack.
FloatAbs()
pushes the absolute value of S1 onto the top of the FP stack. The original value is popped off the stack.
FloatFactorial()
pushes the factorial (x!) of the value in S1 onto the top of the FP stack. The original value is popped off the stack.
FloatNegate()
negates the value in S1 and pushes the result onto the top of the FP stack. The
original value is popped off the stack.
FloatInverse()
pushes the inverse of the value in S1 (-S1) onto the top of the FP stack. The original value is popped off of the stack.
FloatMax(), FloatMin(), FloatLt0(), FloatEq0(), FloatGt0()
FloatMax()
performs a compare of the FP numbers in S1 and S2 and, if necessary, swaps the greater number into S1.
FloatMin()
performs a compare of the FP numbers in S1 and S2 and, if necessary, swaps the lesser number into S1.
FloatLt0(), FloatEq0(), FloatGt0()
check whether the FP number in S1 is less than zero, equal to zero, or greater than zero. The carry bit is set to TRUE if the relationship is true. The original value is popped off of the stack.
FloatFrac(), FloatTrunc(), FloatInt(), FloatIntFrac(), FloatRound()
FloatFrac()
pushes the fractional portion of S1 onto the FP stack. The original value is popped off of the stack.
FloatTrunc()
pushes the integral portion of the contents of S1 onto the FP stack. This amounts to a rounding of the FP number toward zero, so that
FloatTrunc()
performed on -7.8 would return -7. The original value is popped off the stack.
FloatInt()
rounds S1 down to its integral component, so that
FloatInt()
performed on -7.8 would return -8. Note that for negative numbers, this is different from
FloatTrunc()
. The original value is popped off the stack.
FloatIntFrac()
splits a number into its fractional and integral parts, with the fractional part in S1 and the integral part in S2. The original value is popped off the stack.
FloatRound()
rounds S1 to a given number of decimal places.
FloatRound()
passed with zero as an argument rounds S1 to the nearest integer, rounding up if greater than or equal to .5, rounding down if less than .5
FloatRandom(), FloatRandomN(), FloatRandomize()
FloatRandom()
pushes a random number between 0 (inclusive) and 1 (exclusive) onto the stack.
FloatRandomN()
pushes a random integer between 0 (inclusive) and N (exclusive) onto the stack.
FloatRandomize()
primes the random number generator. This routine expects a seed and some
RandomGenInitFlags
. If the flag RGIF_USE_SEED is passed, then a developer-supplied seed will be used. Otherwise, a seed based on the timer clock will be used.
FloatRandomize()
should always be called before any of the
FloatRandom()
routines to ensure a high degree of randomness.
The random number generation method uses the linear congruential method, an algorithm which ensures a high degree of randomness in the computation method.
Transcendental functions are functions that cannot be constructed using normal arithmetic routines. Care must be taken with the following routines to ensure that they are operating on a valid range of values. Otherwise, the routines will return an error.
FloatSin(), FloatCos(), FloatTan(), FloatArcSin(), FloatArcCos(), FloatArcTan(), FloatArcTan2(), FloatSinh(), FloatCosh(), FloatTanh(), FloatArcSinh(), FloatArcCosh(), FloatArcTanh()
FloatSin(), FloatCos()
, and
FloatTan()
perform the given operation on the contents of S1, pushing the result onto the FP stack. S1 must be expressed in radians. The original value is popped off of the stack.
FloatArcSin(), FloatArcCos()
, and
FloatArcTan()
perform
the given inverse operation on S1, pushing the result onto the FP stack. The result is given in radians. The original value is popped off of the stack.
FloatArcTan2()
calculates the arc tangent given cartesian coordinates
x
and
y
. The arctangent is calculated from the x-axis through the origin to the given point. The value returned is expressed in radians, between -p (exclusive) and +p (inclusive). The original values are popped off of the stack.
FloatSinh(), FloatCosh()
, and
FloatTanh()
perform the given hyperbolic operations on S1, pushing the result onto the FP stack. The original value is popped off of the stack.
FloatArcSinh(), FloatArcCosh()
, and
FloatArcTanh()
perform the given inverse hyperbolic operations on S1, pushing the result onto the FP stack. The original value is popped off of the stack.
FloatExp(), FloatExponential(), FloatLg(), FloatLog(), FloatLn(), FloatLn1plusX(), FloatSqr(), FloatSqrt()
FloatExp()
performs the exponentiation of
e
to the power of S1, pushing the result onto the FP stack. The original value is popped off of the stack.
FloatExponential()
performs the exponentiation of S2 to the power of S1, pushing the result onto the FP stack. The original values are popped off of the stack.
FloatLg()
performs the logarithm to the base 2 on S1, pushing the result onto the FP stack. The original value is popped off of the stack.
FloatLog()
performs the logarithm to the base 10 on S1. The original value is popped off the stack.
FloatLn()
performs the natural logarithm (log base
e
) on S1. The original value is popped off the stack.
FloatLn1plusX()
performs the natural log of (1 + S1). The original value is popped off the stack.
FloatSqr()
performs the square of S1, pushing
the result onto the FP stack. The original value is popped off the stack.
FloatSqrt()
performs the square root of S1, pushing the result onto the FP stack. The original value is popped off the stack.
At some point 80-bit FP numbers may need to be converted into different formats, or you may need to convert these differently formatted numbers into a floating point representation. The Math Library provides for this contingency.
FloatDwordToFloat(), FloatWordToFloat(), FloatFloatToDword()
FloatDwordToFloat()
converts a passed double-word signed integer into a floating point number, and pushes that number onto the FP stack.
FloatWordToFloat()
converts
a passed word signed integer into a floating point number, and pushes that number onto the FP stack.
FloatFloatToDword()
converts the FP number in S1 into a double-word signed integer. The FP number is converted into an integer by rounding the FP number to zero decimal places.
You can convert a FP number into a word value by using
FloatFloatToDword()
and just using the low word.
FloatGeos80ToIEEE64(), FloatGeos80ToIEEE32(), FloatIEEE64ToGeos80(), FloatIEEE32ToGeos80()
FloatGeos80ToIEEE64()
converts a GEOS FP number (80 bits) into a 64 bit floating point number (in C, a type of
double
).
FloatGeos80ToIEEE32()
converts a GEOS FP number (80 bits) into a 32 bit floating point number (in C, a type of
float
).
FloatIEEE64ToGeos80()
converts a 64 bit FP number into a GEOS 80 bit FP number (in C, a type of
long
double
).
FloatIEEE32ToGeos80()
converts a 32 bit FP number into a GEOS 80 bit FP number (in C, a type of
long
double
).