Delphi:如何翻译这个C代码,它对IEEE浮点数进行低级访问?

时间:2015-06-26 07:16:44

标签: delphi

以下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专家愿意提供帮助。

更多信息

我稍后会添加此部分,这不是问题的一部分。本节为读者提供了更准确的信息。

4 个答案:

答案 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;