以下C函数来自fastapprox项目。
static inline float
fasterlog2 (float x)
{
union { float f; uint32_t i; } vx = { x };
float y = vx.i;
y *= 1.1920928955078125e-7f;
return y - 126.94269504f;
}
我知道C union可以翻译成Delphi变体记录,但是我仍然难以将这种低级C代码翻译成Delphi。我希望这里的Delphi专家愿意提供帮助。
我稍后会添加此部分,这不是问题的一部分。本节为读者提供了更准确的信息。
fasterlog2()
被故意设计为更简单,更快但不太准确的Log2功能。任何期望更高准确度的人都可以使用他们提供的更准确的功能,即fastlog2()
。126.94269504
。 Mathematica website为.nb
文件提供免费查看器。答案 0 :(得分:8)
我想我会通过使用指针强制转换来对其进行编码以实现重新解释转换:
function fasterlog2(x: single): single;
const
c1: Single = 1.1920928955078125e-7;
c2: Single = 126.94269504;
var
y: single;
begin
y := PCardinal(@x)^;
Result := y * c1 - c2;
end;
请注意,我使用类型single
的类型常量来确保与C代码完全匹配。
我真的不认为在Delphi实现中需要变体记录。
或者你可以使用纯粹的asm方法。 x86版本如下所示:
function fasterlog2asm(x: single): single;
const
c1: Single = 1.1920928955078125e-7;
c2: Single = 126.94269504;
asm
FILD DWORD PTR [ESP+$08]
FMUL c1
FSUB c2
FWAIT
end;
对于x64,SSE实现将是
function fasterlog2asm64(x: single): single;
const
c1: double = 1.1920928955078125e-7;
c2: double = 126.94269504;
asm
CVTDQ2PD xmm0, xmm0
MULSD xmm0, c1
SUBSD xmm0, c2
CVTSD2SS xmm0, xmm0
end;
在x64中,汇编版本的性能仅为纯pascal函数的两倍。 x86汇编版本的性能超过了五倍 - 这完全是由于SSE与x87中类型转换(整数/单/双)的成本较高。
可以使用此方法的原因是浮点数表示为
significand * base^exponent
和值2用作基础。
答案 1 :(得分:5)
这个怎么样:
function fasterlog2(x: Single): Single; inline;
const
f1: Single = 1.1920928955078125e-7;
f2: Single = -126.94269504;
var
i: Cardinal absolute x;
begin
Result := i * f1 + f2;
end;
答案 2 :(得分:4)
可能的翻译是:
function fasterlog2(x: Single): Single;
type
TVx = record
case Byte of
0: (f: Single);
1: (i: UInt32); // Or Cardinal, depending on version
end;
const
C1: Single = 1.1920928955078125e-7;
C2: Single = 126.94269504;
var
y: Single;
vx: TVx;
begin
vx.f := x;
y := vx.i;
y := y * C1;
Result := y - C2;
end;
我认为这会以某种方式使用Single的位模式的知识。我不确定它是否真的能给你一个更快的日志,但这就是C例程正在做的事情。
答案 3 :(得分:4)
试试这个(直译):
function fasterlog2(x : Single): Single; inline;
type
TX = record
case boolean of
false: (f : Single);
true: (i : Cardinal);
end;
const
f1 : Single = 1.1920928955078125e-7;
f2 : Single = -126.94269504;
var
vx: TX absolute x;
y: Single;
begin
y := vx.i;
y := y * f1;
Result := y + f2;
end;
WriteLn(fasterlog2( 1024.0));
WriteLn(Math.Log2( 1024.0));
输出:
1.00573043823242E+0001
1.00000000000000E+0001
或oneliner(类似于Davids示例):
function fasterlog2(x : Single): Single; inline;
const
f1 : Single = 1.1920928955078125e-7;
f2 : Single = -126.94269504;
begin
Result := PCardinal(@x)^ * f1 + f2;
end;