Fixed point math routines

Allegro provides some routines for working with fixed point numbers, and defines the type 'fixed' to be a signed 32 bit integer. The high word is used for the integer part and the low word for the fraction, giving a range of -32768 to 32767 and an accuracy of about four or five decimal places. Fixed point numbers can be assigned, compared, added, subtracted, negated and shifted (for multiplying or dividing by powers of two) using the normal integer operators, but you should take care to use the appropriate conversion routines when mixing fixed point with integer or floating point values. Writing 'fixed_point_1 + fixed_point_2' is ok, but 'fixed_point + integer' is not.

fixed itofix(int x);
Converts an integer to fixed point. This is the same thing as x<<16.

int fixtoi(fixed x);
Converts fixed point to integer, rounding as required.

int fixfloor(fixed x);
Returns the greatest integer not greater than x. That is, it rounds towards negative infinity.

int fixceil(fixed x);
Returns the smallest integer not less than x. That is, it rounds towards positive infinity.

fixed ftofix(float x);
Converts a floating point value to fixed point.

float fixtof(fixed x);
Converts fixed point to floating point.

fixed fixmul(fixed x, fixed y);
A fixed point value can be multiplied or divided by an integer with the normal '*' and '/' operators. To multiply two fixed point values, though, you must use this function.

If an overflow or division by zero occurs, errno will be set and the maximum possible value will be returned, but errno is not cleared if the operation is successful. This means that if you are going to test for overflow you should set errno=0 before calling fixmul().

fixed fixdiv(fixed x, fixed y);
Fixed point division: see comments about fixmul().

fixed fixadd(fixed x, fixed y);
Although fixed point numbers can be added with the normal '+' integer operator, that doesn't provide any protection against overflow. If overflow is a problem, you should use this function instead. It is slower than using integer operators, but if an overflow occurs it will clamp the result, rather than just letting it wrap, and set errno.

fixed fixsub(fixed x, fixed y);
Fixed point subtraction: see comments about fixadd().

The fixed point square root, sin, cos, tan, inverse sin, and inverse cos functions are implemented using lookup tables, which are very fast but not particularly accurate. At the moment the inverse tan uses an iterative search on the tan table, so it is a lot slower than the others.

Angles are represented in a binary format with 256 equal to a full circle, 64 being a right angle and so on. This has the advantage that a simple bitwise 'and' can be used to keep the angle within the range zero to a full circle, eliminating all those tiresome 'if (angle >= 360)' checks.

fixed fixsin(fixed x);
Lookup table sine.

fixed fixcos(fixed x);
Lookup table cosine.

fixed fixtan(fixed x);
Lookup table tangent.

fixed fixasin(fixed x);
Lookup table inverse sine.

fixed fixacos(fixed x);
Lookup table inverse cosine.

fixed fixatan(fixed x);
Fixed point inverse tangent.

fixed fixatan2(fixed y, fixed x);
Fixed point version of the libc atan2() routine.

fixed fixsqrt(fixed x);
Fixed point square root.

fixed fixhypot(fixed x, fixed y);
Fixed point hypotenuse (returns the square root of x*x + y*y).

If you are programming in C++ you can ignore all the above and use the fix class instead, which overloads a lot of operators to provide automatic conversion to and from integer and floating point values, and calls the above routines as they are required. You should not mix the fix class with the fixed typedef though, because the compiler will mistake the fixed values for regular integers and insert unnecessary conversions. For example, if x is an object of class fix, calling fixsqrt(x) will return the wrong result. You should use the overloaded sqrt(x) or x.sqrt() instead.

The fixed point functions used to be named with an "f" prefix instead of "fix", eg. fixsqrt() used to be fsqrt(), but were renamed due to conflicts with some libc implementations. This should not affect most existing code as there are backwards compatibility aliases. These aliases are static inline functions which map the old names to the new names, eg. fsqrt() calls fixsqrt(). You can disable the aliases by defining the preprocessor macro ALLEGRO_NO_FIX_ALIASES before including allegro.h.