📄 fmaths.inc
字号:
{* ______ ___ ___
* /\ _ \ /\_ \ /\_ \
* \ \ \L\ \\//\ \ \//\ \ __ __ _ __ ___
* \ \ __ \ \ \ \ \ \ \ /'__`\ /'_ `\/\`'__\/ __`\
* \ \ \/\ \ \_\ \_ \_\ \_/\ __//\ \L\ \ \ \//\ \L\ \
* \ \_\ \_\/\____\/\____\ \____\ \____ \ \_\\ \____/
* \/_/\/_/\/____/\/____/\/____/\/___L\ \/_/ \/___/
* /\____/
* \_/__/
*
* Fixed point math inline functions (generic C).
*
* By Shawn Hargreaves.
*
* See readme.txt for copyright information.
*}
{$IFDEF ALLEGRO_INTERFACE}
const
ERANGE = 1;
EDOM = 2;
// ftofix and fixtof are used in generic C versions of fixmul and fixdiv
function ftofix(x: double): fixed;
function fixtof(x: fixed): double;
function fixadd(x, y: fixed): fixed;
function fixsub(x, y: fixed): fixed;
function fixmul(x, y: fixed): fixed;
function fixdiv(x, y: fixed): fixed;
function fixfloor(x: fixed): sint32;
function fixceil(x: fixed): sint32;
function itofix(x: sint32): fixed;
function fixtoi(x: fixed): sint32;
function fixcos(x: fixed): fixed;
function fixsin(x: fixed): fixed;
function fixtan(x: fixed): fixed;
function fixacos(x: fixed): fixed;
function fixasin(x: fixed): fixed;
{$ENDIF ALLEGRO_INTERFACE}
{$IFDEF ALLEGRO_IMPLEMENTATION}
function ftofix(x: double): fixed;
begin
if x > 32767.0 then
begin
allegro_errno^[0] := ERANGE;
Result := $7FFFFFFF;
Exit;
end;
if x < -32767.0 then
begin
allegro_errno^[0] := ERANGE;
Result := $7FFFFFFF;
Exit;
end;
if x < 0 then
Result := Round(x * 65536.0 + -0.5)
else
Result := Round(x * 65536.0 + 0.5);
end;
function fixtof(x: fixed): double;
begin
Result := x / 65536.0;
end;
procedure _set_errno_erange;
begin
allegro_errno^[0] := ERANGE;
end;
// use generic C versions
function fixadd(x, y: fixed): fixed; assembler;
asm
add eax, y
jno @Out1
call _set_errno_erange
mov eax, $7FFFFFFF
cmp y, 0
jg @Out1
neg eax
@Out1:
end;
{
begin
Result := x + y;
if result >= 0 then
begin
if (x < 0) and (y < 0) then
begin
allegro_errno^[0] := ERANGE;
Result := -$7FFFFFFF;
end;
end else
begin
if (x > 0) and (y > 0) then
begin
allegro_errno^[0] := ERANGE;
Result := $7FFFFFFF;
end
end;
end;
}
function fixsub(x, y: fixed): fixed; assembler;
asm
sub eax, y
jno @Out1
call _set_errno_erange
mov eax, $7FFFFFFF
cmp y, 0
jl @Out1
neg eax
@Out1:
end;
{
begin
Result := x - y;
if Result >= 0 then
begin
if (x < 0) and (y > 0) then
begin
allegro_errno^[0] := ERANGE;
Result := -$7FFFFFFF;
end;
end else
begin
if (x > 0) and (y < 0) then
begin
allegro_errno^[0] := ERANGE;
Result := $7FFFFFFF;
end;
end;
end;
}
{* 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.
*}
function fixmul(x, y: fixed): fixed; assembler;
begin
Result := ftofix(fixtof(x) * fixtof(y));
end;
function fixdiv(x, y: fixed): fixed;
begin
if y = 0 then
begin
allegro_errno^[0] := ERANGE;
if x < 0 then
Result := -$7FFFFFFF
else
Result := $7FFFFFFF;
end else
Result := ftofix(fixtof(x) / fixtof(y));
end;
function fixfloor(x: fixed): sint32; assembler;
asm
sar eax, $10
end;
{
begin
// (x >> 16) is not portable
if x >= 0 then
Result := x shr 16
else
Result := not((not x) shr 16);
end;
}
function fixceil(x: fixed): sint32;
begin
if x > $7FFF0000 then
begin
allegro_errno^[0] := ERANGE;
Result := $7FFF;
Exit;
end;
Result := fixfloor(x + $FFFF);
end;
function itofix(x: sint32): fixed;
begin
Result := x shl 16;
end;
function fixtoi(x: fixed): sint32;
begin
Result := fixfloor(x) + ((x and $8000) shr 15);
end;
function fixcos(x: fixed): fixed;
begin
Result := _cos_tbl[((x + $4000) shr 15) and $1FF];
end;
function fixsin(x: fixed): fixed;
begin
Result := _cos_tbl[((x - $400000 + $4000) shr 15) and $1FF];
end;
function fixtan(x: fixed): fixed;
begin
Result := _tan_tbl[((x + $4000) shr 15) and $FF];
end;
function fixacos(x: fixed): fixed;
begin
if (x < -65536) or (x > 65536) then
begin
allegro_errno^[0] := EDOM;
Result := 0;
Exit;
end;
Result := _acos_tbl[(x+65536+127) shr 8];
end;
function fixasin(x: fixed): fixed;
begin
if (x < -65536) or (x > 65536) then
begin
allegro_errno^[0] := EDOM;
Result := 0;
Exit;
end;
Result := $00400000 - _acos_tbl[(x+65536+127)shr 8];
end;
{$ENDIF ALLEGRO_IMPLEMENTATION}
{$IFDEF ALLEGRO_LOADVARIABLE}
{$ENDIF ALLEGRO_LOADVARIABLE}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -