http://www.developer.com/
Understanding Floating Point Math Under Palm OSMarch 11, 2005 IntroductionWhen I first started to play around with Palm OS programming, I was surprised that floating point support is highly restricted, supporting only basic math. Even though I understood that, due to obvious system restrictions, a reasonable part of the standard C library was not implemented as either a part of OS or a separate library, my senses have not afforded it quietly. Initially, Palm OS has supported only 16-bit precision math; then, NewFloatMgr.lib and fp68k.lib came out for 32- and 64-bit floating point arithmetic. There also was MathLib for more complicated functions that you usually find inside math.h of the standard C library. Fortunately, the latest versions of Palm OS made a step forward. You do not need to explicitly link any additional libs for basic math. Moreover, programs such as CodeWarrior produce the necessary code to use MathLib via Project Wizard. Palm OS Developer Suite (PODS) has exposed new APIs for Palm OS Garnet and Cobalt. Nevertheless, you still can't use the %f-like style to format a floating point value to its string reprpesentation... Basic Types: Getting StartedThe good news is that you can freely use standard C types: double and float. Float is a 32-bit value, whereas double is a 64-bit length. The compiler will automatically generate the appropriate calls to the required libs. So, the following code will work just fine: double d1, d2, d3; d1 = 1.205; d2 = 2.32; d3 = d1 / d2; In addition, the MathLib library also operates with doubles as function arguments and return values. Hence, if you need to carry out some floating point calculations without any conversions to other types, you're perfectly set up. In reality, you will want to manipulate these simple decimal numbers and get additional information about them. I believe this article will help you to understand which options you have with floating point math under Palm OS. You can start exploring Palm OS Float Manager by taking a look at its data structures: typedef Int32 FlpFloat; typedef _sfpe_64_bits FlpDouble; typedef _sfpe_64_bits FlpLongDouble; typedef struct { UInt32 sign : 1; Int32 exp : 11; UInt32 manH : 20; UInt32 manL; } FlpDoubleBits; // for accessing specific fields typedef union { double d; // for easy assignment of values FlpDouble fd; // for calling New Floating point // manager routines UInt32 ul[2]; // for accessing upper and lower longs FlpDoubleBits fdb; // for accessing specific fields } FlpCompDouble; typedef union { float f; // for easy assignment of values FlpFloat ff; // for calling New Floating point // manager routines UInt32 ul; // for accessing bits of the float } FlpCompFloat; The preceding structures give you a convenient mechanism for transparent manipulations over floating point data. For instance, you can assign double values to a variable of FlpCompDouble type, send it to Float Manager functions, get access to specific bits, and so forth. Later in this article, you'll use them a lot. The tiny code snippet below just shows the common manner: double d1, d2, d3; char szBuffer[32]; FlpCompDouble fd; d1 = 1.205; d2 = 2.32; d3 = d1 / d2; fd.d = d3; FlpFToA(fd.fd, szBuffer); Running Simple Arithmetic OperationsAfter basic data types are defined, you are ready to investigate what functions Palm OS Float Manager supports. First, there are several useful macros that you can use to manipulate a variable of FlpCompDouble type: #define FlpGetSign(x) ((__HI32(x) & 0x80000000) != 0) #define FlpIsZero(x) ( ((__HI32(x) & 0x7fffffff) | (__LO32(x))) == 0) #define FlpGetExponent(x) (((__HI32(x) & 0x7ff00000) >> 20) - 1023) #define FlpNegate(x) (((FlpCompDouble *)&x)->ul[__HIX] ^= 0x80000000) #define FlpSetNegative(x) (((FlpCompDouble *)&x)->ul[__HIX] | = 0x80000000) #define FlpSetPositive(x) (((FlpCompDouble *)&x)->ul[__HIX] & = ~0x80000000) With these macros, you can conduct sign and zero checks, set or reset the variable's sign, and so forth. The next group of functions is quite standard: Err FlpBase10Info(FlpDouble a, UInt32 *mantissaP, Int16 *exponentP, Int16 *signP) Err FlpFToA(FlpDouble a, Char *s) FlpDouble FlpAToF(const Char *s) FlpDouble FlpCorrectedAdd(FlpDouble firstOperand, FlpDouble secondOperand, Int16 howAccurate) FlpDouble FlpCorrectedSub(FlpDouble firstOperand, FlpDouble secondOperand, Int16 howAccurate) FlpBase10Info returns detailed info about a floating number. Note here that, as it's referred to in the SDK documentation, FlpBase10Info reports that zero is a "negative" value. To avoid mistakes, you should check the sign and mantissa instead of just the sign. FlpFToA and FlpAToF make conversions between decimal and string representations of a floating point value. When you convert such a number to a string, the output buffer will be filled by text in a form such as "5.1939655e-01". That is not always an acceptable result. In many cases, you will want to produce more attractive formats; for example, "5.20" and so forth. Unfortunately, Palm OS still has no support for '%f'-like formatting. Nevertheless, you can find useful implementations of good workarounds all over the Web; for example, here. Well, based on the appropriate example, you may always enjoy writing your own formatting function... The two last functions enable you to obtain the result of addition to subtraction with desired accuracy. When operands are similar, the result can be very small. In fact, it possibly should be zero if the result's exponent and that of the operands is close to the number of significant bits expressible by the mantissa. Due to difficulty in representing a fractional part as a binary value, after several calculations errors can appear in significant bits. The howAccurate parameter gives you a control over forcing the result to be zero. Supplying 0 here will produce a default level of accuracy, which is equivalent to howAccurate = 48 (bits). Performing Standard Operations on Floating NumbersSoftware Floating Point Emulator(sfpe) under Palm OS keeps status of floating operations. You can obtain and set it by calling the following functions: /* * Constants passed to _fp_set_fpscr and received from * _fp_get_fpscr are: * flpInvalid, flpOverflow, flpUnderflow, flpDivByZero, * or flpInexact */ Int32 _fp_get_fpscr(void) void _fp_set_fpscr(Int32) That is a good way to check for errors; for example, division by zero or overflow. Additional explanations on what IEEE floating point errors are can be found, for instance, here at Sun's site. Float Manager offers a long list of functions used for various conversions between numeric types, comparisons, and basic calculations. For conversions, you will find the following comment in FloatMgr.h: /* * The shorthand here can be determined from the context: * i --> long (Int32) * u --> UInt32 (UInt32) * ll --> long long int * ull --> UInt32 long int * f --> float * d --> double * q --> long double (defaults to double in this implementaton) * XtoY --> map of type X to a value of type Y */ so you will convert something like a UInt32 value to a double by calling: UInt32 nUInt32Val = 10; FlpDouble dblVal = _d_utod(nUInt32Val); A similar block of functions exists for floating point numbers' comparions. I'd like to point out one interesting fact here. Most comparison functions can be defined in the form of _T_Tcmp[e], where T is the argument type. '[e]' versions set a flpInvalid flag in the fpscr register when the operands are unordered. It happens if they have no numerical relationship; in other words, when one or both of them are not numbers, such as with NaN. So, a common rule for comparison functions will be: /* _T_Tcmp[e] * The function shorthand is: * eq --> equal * ne --> not equal * lt --> less than * le --> less than or equal to * gt --> greater than * ge --> greater than or equal to * un --> unordered with * or --> ordered with (i.e. less than, equal to, or greater than) */ The rest of the add/sub/mul/div functions are trivial enough that they should not require an explanation. ConclusionFloating point math is one of the shortcomings with Palm OS. It is getting better with new versions of the OS, so who knows; maybe soon you'll be able to the use standard C library and forget about all that was written above. About the AuthorAlex Gusev started to play with mainframes at the end of the 1980s, using Pascal and REXX, but soon switched to C/C++ and Java on different platforms. When mobile PDAs seriously rose their heads in the IT market, Alex did it too. Now, he works at an international retail software company as a team leader of the Mobile R department, making programmers' lives in the mobile jungles a little bit simpler. |