/* ______ ___ ___ * /\ _ \ /\_ \ /\_ \ * \ \ \L\ \\//\ \ \//\ \ __ __ _ __ ___ * \ \ __ \ \ \ \ \ \ \ /'__`\ /'_ `\/\`'__\/ __`\ * \ \ \/\ \ \_\ \_ \_\ \_/\ __//\ \L\ \ \ \//\ \L\ \ * \ \_\ \_\/\____\/\____\ \____\ \____ \ \_\\ \____/ * \/_/\/_/\/____/\/____/\/____/\/___L\ \/_/ \/___/ * /\____/ * \_/__/ * * Fixed point math inline functions (generic C). * * By Shawn Hargreaves. * * See readme.txt for copyright information. */ #ifndef __al_included_allegro5_inline_fmaths_inl #define __al_included_allegro5_inline_fmaths_inl #include "allegro5/error.h" #ifdef __cplusplus extern "C" { #endif /* al_ftofix and al_fixtof are used in generic C versions of al_fixmul and al_fixdiv */ AL_INLINE(al_fixed, al_ftofix, (double x), { if (x > 32767.0) { al_set_errno(ERANGE); return 0x7FFFFFFF; } if (x < -32767.0) { al_set_errno(ERANGE); return -0x7FFFFFFF; } return (al_fixed)(x * 65536.0 + (x < 0 ? -0.5 : 0.5)); }) AL_INLINE(double, al_fixtof, (al_fixed x), { return (double)x / 65536.0; }) AL_INLINE(al_fixed, al_fixadd, (al_fixed x, al_fixed y), { al_fixed result = x + y; if (result >= 0) { if ((x < 0) && (y < 0)) { al_set_errno(ERANGE); return -0x7FFFFFFF; } else return result; } else { if ((x > 0) && (y > 0)) { al_set_errno(ERANGE); return 0x7FFFFFFF; } else return result; } }) AL_INLINE(al_fixed, al_fixsub, (al_fixed x, al_fixed y), { al_fixed result = x - y; if (result >= 0) { if ((x < 0) && (y > 0)) { al_set_errno(ERANGE); return -0x7FFFFFFF; } else return result; } else { if ((x > 0) && (y < 0)) { al_set_errno(ERANGE); return 0x7FFFFFFF; } else return result; } }) /* In benchmarks conducted circa May 2005 we found that, in the main: * - IA32 machines performed faster with one implementation; * - AMD64 and G4 machines performed faster with another implementation. * * Benchmarks were mainly done with differing versions of gcc. * Results varied with other compilers, optimisation levels, etc. * so this is not optimal, though a tenable compromise. * * Note that the following implementation are NOT what were benchmarked. * We had forgotten to put in overflow detection in those versions. * If you don't need overflow detection then previous versions in the * CVS tree might be worth looking at. * * PS. Don't move the #ifs inside the AL_INLINE; BCC doesn't like it. */ #if (defined ALLEGRO_I386) || (!defined LONG_LONG) AL_INLINE(al_fixed, al_fixmul, (al_fixed x, al_fixed y), { return al_ftofix(al_fixtof(x) * al_fixtof(y)); }) #else AL_INLINE(al_fixed, al_fixmul, (al_fixed x, al_fixed y), { LONG_LONG lx = x; LONG_LONG ly = y; LONG_LONG lres = (lx*ly); if (lres > 0x7FFFFFFF0000LL) { al_set_errno(ERANGE); return 0x7FFFFFFF; } else if (lres < -0x7FFFFFFF0000LL) { al_set_errno(ERANGE); return 0x80000000; } else { int res = lres >> 16; return res; } }) #endif /* al_fixmul() C implementations */ #if (defined ALLEGRO_CFG_NO_FPU) && (defined LONG_LONG) AL_INLINE(al_fixed, al_fixdiv, (al_fixed x, al_fixed y), { LONG_LONG lres = x; if (y == 0) { al_set_errno(ERANGE); return (x < 0) ? -0x7FFFFFFF : 0x7FFFFFFF; } lres <<= 16; lres /= y; if (lres > 0x7FFFFFFF) { al_set_errno(ERANGE); return 0x7FFFFFFF; } else if (lres < -0x7FFFFFFF) { al_set_errno(ERANGE); return 0x80000000; } else { return (al_fixed)(lres); } }) #else AL_INLINE(al_fixed, al_fixdiv, (al_fixed x, al_fixed y), { if (y == 0) { al_set_errno(ERANGE); return (x < 0) ? -0x7FFFFFFF : 0x7FFFFFFF; } else return al_ftofix(al_fixtof(x) / al_fixtof(y)); }) #endif AL_INLINE(int, al_fixfloor, (al_fixed x), { /* (x >> 16) is not portable */ if (x >= 0) return (x >> 16); else return ~((~x) >> 16); }) AL_INLINE(int, al_fixceil, (al_fixed x), { if (x > 0x7FFF0000) { al_set_errno(ERANGE); return 0x7FFF; } return al_fixfloor(x + 0xFFFF); }) AL_INLINE(al_fixed, al_itofix, (int x), { return x << 16; }) AL_INLINE(int, al_fixtoi, (al_fixed x), { return al_fixfloor(x) + ((x & 0x8000) >> 15); }) AL_INLINE(al_fixed, al_fixcos, (al_fixed x), { return _al_fix_cos_tbl[((x + 0x4000) >> 15) & 0x1FF]; }) AL_INLINE(al_fixed, al_fixsin, (al_fixed x), { return _al_fix_cos_tbl[((x - 0x400000 + 0x4000) >> 15) & 0x1FF]; }) AL_INLINE(al_fixed, al_fixtan, (al_fixed x), { return _al_fix_tan_tbl[((x + 0x4000) >> 15) & 0xFF]; }) AL_INLINE(al_fixed, al_fixacos, (al_fixed x), { if ((x < -65536) || (x > 65536)) { al_set_errno(EDOM); return 0; } return _al_fix_acos_tbl[(x+65536+127)>>8]; }) AL_INLINE(al_fixed, al_fixasin, (al_fixed x), { if ((x < -65536) || (x > 65536)) { al_set_errno(EDOM); return 0; } return 0x00400000 - _al_fix_acos_tbl[(x+65536+127)>>8]; }) #ifdef __cplusplus } #endif #endif