level 1
先上结论:
主要避免的行为:
如果重载运算符的返回值类型的 size 很大,那么千万不要让它出现在递归中。这对于函数返回值也一样。
无论重载运算符还是函数,其返回值变量都在栈上,相当于局部变量。
关于高精度:
给高精度数设置一个整型初始值时,不要用 := 的重载。
但做算术运算时,采用运算符重载并不成问题。尽管会发生额外的复制,但因为这个耗时比高精度运算短很多(即使开最高等级的优化,关掉所有检查后也一样),所以不用在意时间消耗。
如果要比较两个高精度数的大小,最好使用比较运算符的重载。
其他
FPC 重载的 := 等价于 Delphi 的 implicit 。准确描述其行为的还是“隐式类型转换”,而不是赋值。
Pascal/Delphi 的赋值操作实际上只会用内建的,不会被替换。
2016年09月08日 19点09分
1
level 1
const
TestTime=1 shl 23;
const
BigRadix=1000000000;
BigWidth=9;
BigIntLen=30;
type
BigInt=array [0..BigIntLen] of LongInt;
operator := (x:LongWord) bx:BigInt;
begin
if x<BigRadix then
begin
bx[0]:=1;
bx[1]:=x;
end
else
begin
bx[0]:=2;
bx[1]:=x mod BigRadix;
bx[2]:=x mod BigRadix;
end;
end;
procedure __assign(var bx:BigInt; x:LongWord);
begin
if x<BigRadix then
begin
bx[0]:=1;
bx[1]:=x;
end
else
begin
bx[0]:=2;
bx[1]:=x mod BigRadix;
bx[2]:=x mod BigRadix;
end;
end;
operator + (const ba, bb:BigInt) bc:BigInt;
var
i, CarryFlag:SizeUInt;
begin
if ba[0]>bb[0] then
bc[0]:=ba[0]
else
bc[0]:=bb[0];
CarryFlag:=0;
for i:=1 to bc[0] do
begin
bc[i]:=ba[i]+bb[i]+CarryFlag;
if bc[i]>=BigRadix then
begin
dec(bc[i], BigRadix);
CarryFlag:=1;
end
else
CarryFlag:=0;
end;
if CarryFlag<>0 then
begin
inc(bc[0]);
bc[bc[0]]:=1;
end;
end;
procedure __AddTo(const ba, bb:BigInt; var bc:BigInt);
var
i, CarryFlag:SizeUInt;
begin
if ba[0]>bb[0] then
bc[0]:=ba[0]
else
bc[0]:=bb[0];
CarryFlag:=0;
for i:=1 to bc[0] do
begin
bc[i]:=ba[i]+bb[i]+CarryFlag;
if bc[i]>=BigRadix then
begin
dec(bc[i], BigRadix);
CarryFlag:=1;
end
else
CarryFlag:=0;
end;
if CarryFlag<>0 then
begin
inc(bc[0]);
bc[bc[0]]:=1;
end;
end;
function GetTick:LongWord;
var
OriSeed:LongWord;
begin
OriSeed:=RandSeed;
Randomize;
GetTick:=RandSeed;
RandSeed:=OriSeed;
end;
procedure SetRandomBigInt(var b:BigInt);
var
j:LongWord;
begin
b[0]:=1+Random(BigIntLen-1);{ 1..BigIntLen-1 }
for j:=1 to b[0]-1 do
b[j]:=Random(BigRadix);{ 0..BigRadix-1 }
inc(j);
b[j]:=1+Random(BigRadix-1);{ 1..BigRadix-1 }
FillDWord(b[j+1], BigIntLen-j, 0); { Reset highest bits }
end;
var
ClockStart, ClockEnd:LongWord;
i, j:LongWord;
vBigA, vBigB, vBigC:BigInt;
begin
{ Operator Implicit }
ClockStart:=GetTick;
Randomize;
for i:=1 to TestTime do
vBigA:=Random(BigRadix*2);
ClockEnd:=GetTick;
writeln('operator.assign, ', ClockEnd-ClockStart);
{ Procedure __Assign }
ClockStart:=GetTick;
Randomize;
for i:=1 to TestTime do
__Assign(vBigA, Random(BigRadix*2));
ClockEnd:=GetTick;
writeln('procedure.__assign, ', ClockEnd-ClockStart);
//Randomize;
//SetRandomBigInt(vBigA);
//SetRandomBigInt(vBigB);
{ Operator Add }
ClockStart:=GetTick;
Randomize;
for i:=1 to TestTime do
begin
SetRandomBigInt(vBigA);
SetRandomBigInt(vBigB);
vBigC:=vBigA+vBigB;
end;
ClockEnd:=GetTick;
writeln('opeartor.add, ', ClockEnd-ClockStart);
{ Procedure __Add }
ClockStart:=GetTick;
Randomize;
for i:=1 to TestTime do
begin
SetRandomBigInt(vBigA);
SetRandomBigInt(vBigB);
__AddTo(vBigA, vBigB, vBigC);
end;
ClockEnd:=GetTick;
writeln('procedure.__AddTo, ', ClockEnd-ClockStart);
end.
这个程序会体现出重载运算符在高精度运算中对效率的影响。
可以通过改变 TestTime 和 BigIntLen 的值发现一些有趣的现象。
似乎存在 + 比 __AddTo 还快的情况。这可能与栈和缓存的关系有关。
(运行时间在 15s 以上时可能会出现,但未在运行时间 1s 以下的情况中复现)
2016年09月08日 19点09分
2