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.