Delphi中提供了三个函数: Trunc: 将浮点数的整数部分返回。 在D2007 + Win7 + Intel平台下测试, 由于Int返回的并非整型,我们把它排除掉。测试结果是 Round 性能高出Trunc好几倍。 为什么Trunc慢了呢,查看了System中的ASM代码,Trunc有11行指令,Round则只有5行。 出于好奇,通过网络研究下Trunc的算法,知道了在 IEEE 754 里规定双精度浮点数是 64 位,而其中符号位占 1 位,指数部分是 11 位,那么尾数部分就是 52 位,那么 1 * 2^52 = $10000000000000。设要取整的浮点数为V, 那么通过符点数运算的对阶法, PInteger(V + $10000000000000 的结果)^ 就是计算结果了。 于是有了下面的函数: function MyTrunc(const V: Double): Integer; inline; var D: Double; begin if V > 0 then D := V - 0.499999999999 + $18000000000000 else D := V + 0.499999999999 + $18000000000000; Result := PInteger(@D)^; end; 再来测试看看: procedure TForm1.Button6Click(Sender: TObject); const C = 100000000; var u: Extended; I: Integer; v, v1, v2: Integer; t, t1, t2: Int64; begin u := Now; t := GetTimestamp; for I := 0 to C - 1 do v := Trunc(u); t := GetTimestamp - t; t1 := GetTimestamp; for I := 0 to C - 1 do v1 := Round(u); t1 := GetTimestamp - t1; t2 := GetTimestamp; for I := 0 to C - 1 do v2 := MyTrunc(u); t2 := GetTimestamp - t2; ShowMessage(Format('Trunc: %dms, Round: %dms, MyTrunc: %dms'#13'%d, %d, %d', [t, t1, t2, v, v1, v2])); end;
// 注: GetTimestamp 为高精度计时器返回单位为ms(与QWorker中的返回0.1ms有所不同),可换成GetTickCount。 测试结果如下:
哈哈,居然成了最快的了。 要注意的是, MyTrunc 我这里并没有像System的Trunc那样写汇编代码,一是那样不能inline了,二是通过观测编译结果发现已经是很简单的几行指令了。 通过对MyTrunc简单个修改下,成了MyRound:
function MyRound(const V: Double): Integer; inline; var D: Double; begin D := V + $18000000000000; Result := PInteger(@D)^; end;
参考资料: http://www.360doc.com/content/14/0308/22/3520047_358884576.shtml
|
2023-10-27
2022-08-15
2022-08-17
2022-09-23
2022-08-13
请发表评论