Delphi中如何实现类似C代码对IEEE浮点数的底层操作?

2026-04-10 18:433阅读0评论SEO资讯
  • 内容介绍
  • 文章标签
  • 相关推荐

本文共计493个文字,预计阅读时间需要2分钟。

Delphi中如何实现类似C代码对IEEE浮点数的底层操作?

使用C函数自`fastapprox`项目实现`static inline float fasterlog2(float x)`,代码如下:

cunion { float f; uint32_t i;} vx={x};float y=vx.i * 1.1920928955078125e-7f;return y - 126.94269504f;

Delphi中如何实现类似C代码对IEEE浮点数的底层操作?

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

更多信息

我稍后会添加此部分,这不是问题的一部分.本节为希望获得更高准确性的读者提供信息.

>在fastapprox中,quicklog2()被故意设计为更简单,更快但不太准确的Log2功能.任何期望更高准确度的人都可以使用他们提供的更准确的功能,即fastlog2().
>他们包括一个Mathematica notebook,解释了他们的算法以及一些神秘值的起源,例如:126.94269504. Mathematica website为.nb文件提供免费查看器.
>另见:Why the IEEE-754 exponent bias used in this C code is 126.94269504 instead of 127?

我想我会通过使用指针强制转换来对其进行编码以实现重新解释转换:

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;

请注意,我使用了type类型的类型常量来确保与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用作基础.

本文共计493个文字,预计阅读时间需要2分钟。

Delphi中如何实现类似C代码对IEEE浮点数的底层操作?

使用C函数自`fastapprox`项目实现`static inline float fasterlog2(float x)`,代码如下:

cunion { float f; uint32_t i;} vx={x};float y=vx.i * 1.1920928955078125e-7f;return y - 126.94269504f;

Delphi中如何实现类似C代码对IEEE浮点数的底层操作?

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

更多信息

我稍后会添加此部分,这不是问题的一部分.本节为希望获得更高准确性的读者提供信息.

>在fastapprox中,quicklog2()被故意设计为更简单,更快但不太准确的Log2功能.任何期望更高准确度的人都可以使用他们提供的更准确的功能,即fastlog2().
>他们包括一个Mathematica notebook,解释了他们的算法以及一些神秘值的起源,例如:126.94269504. Mathematica website为.nb文件提供免费查看器.
>另见:Why the IEEE-754 exponent bias used in this C code is 126.94269504 instead of 127?

我想我会通过使用指针强制转换来对其进行编码以实现重新解释转换:

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;

请注意,我使用了type类型的类型常量来确保与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用作基础.