program arctan2_speed;

uses

sysutils, math;

function PaoloArcTan2(Y, X : Float) : Float;

begin

if (X = 0) then begin

if (Y = 0) then

Result:=0

else

if (Y > 0) then

Result:=0.5*pi

else

Result:=-0.5*pi

end

else begin

Result:=ArcTan(Y/X);

if (X < 0) then

if (Y < 0) then

Result:=Result-pi

else

Result:=Result+pi

end;

end;

function JoshArcTan2 (y, x : Float) : Float;

begin

if x>0.0 then result := arctan (y/x) // using /2 rather than *.5 as old school / was generally more accurate and for me more readable

else if x <0.0 then result := arctan (y/x) + pi

else if y<>0.0 then result := pi/2 * sign (y)

else result:=0.0;// x=0 so if y is also 0.0 result stays set at 0.00 else result gets set +-pi/2

if result>pi then result:=result-2*pi; // keep result in range of +- pi could use abs sign here but then would be used every iteration of function

end;

const

MILLION = 1000 * 1000;

N = 20 * MILLION;

type

TArcTan2Func = function(y, x: Float): Float;

TData = record

x, y: Float;

end;

var

Data: array[1..n] of TData;

j,m,p:Array[1..n] of Float;

procedure PrepareData(fixx,fixy:boolean;v:float);

var

i: Integer;

begin

// randomize;

RandSeed := 0;

for i := 1 to N do

begin

if fixx then Data[i].X:=v else Data[i].X := (Random()-0.5) * high(dword);

if fixy then Data[i].Y:=v else Data[i].Y := (Random()-0.5) * high(dword);

end;

end;

procedure Test(Descr: String; ArcTan2Func: TArcTan2Func; w:string);

var

t: TDateTime;

i: Integer;

ans:float;

begin

Write('Testing ' + Descr + '...');

RandSeed := 0;

t := Now;

for i := 1 to N do

begin

ans:=ArcTan2Func(Data[i].Y, Data[i].X);

case w of

'j':j[i]:=ans;

'm':m[i]:=ans;

'p':p[i]:=ans;

end;

end;

t := Now - t;

WriteLn(' done: ', FormatDateTime('s.zzz', t), ' sec');

end;

var l,c:integer;

td:extended;

procedure reportresult(heading:string);

begin

WriteLn(heading);

Test('Math.arctan2', @arctan2,'m');

Test('PaoloArctan2', @paoloarctan2,'p');

Test('JoshArctan2', @josharctan2,'j');

WriteLn('Finished. Calculating');

Write('Check difference between math and Josh');

c:=0;

td:=0;

for l:=1 to n do

begin

if j[l]<>m[l] then

begin

inc(c);

td:=td+abs(j[l]-m[l]);

end;

end;

if c=0 then WriteLn(' No Differences')

else

begin

WriteLn;

WriteLn(' Differences '+c.ToString+'Total of Differences ',td:4:16);

end;

Write('Check difference between math and Paolo');

c:=0;

td:=0;

for l:=1 to n do

begin

if m[l]<>p[l] then

begin

inc(c);

td:=td+abs(p[l]-m[l]);

end;

end;

if c=0 then WriteLn(' No Differences')

else

begin

WriteLn;

WriteLn(' Differences '+c.ToString+'Total of Differences ',td:4:16);

end;

Writeln('** End of Test ** '+heading);

Writeln;Writeln;

end;

begin

randomize;

PrepareData(false,false,0);

reportresult('Calculating and testing '+n.ToString+' random x & y samples');

PrepareData(true,false,0);

reportresult('Calculating and testing '+n.ToString+' samples with all x=0');

PrepareData(false,true,0);

reportresult('Calculating and testing '+n.ToString+' samples with all y=0');

PrepareData(true,true,0);

reportresult('Calculating and testing '+n.ToString+' samples with all y=0 and x=0');

ReadLn;

end.