如何操作(快速)尾数和指数部分的double或浮点数在c ++?

时间:2016-01-09 19:40:57

标签: c++ ieee-754 exponent numerical-computing mantissa

我使用c ++来计算各种类型的特殊函数(例如Lambert函数,用于评估反演的迭代方法等)。在许多情况下,直接使用尾数和指数有明显更好的方法。

我找到了许多答案,如何提取尾数和指数部分,但是所有这些都只是"具有不太有效的计算速度的学术案例"对我来说有点无用(我用尾数和指数操作的动机是提高计算速度)。有时我需要调用一些特定功能大约十亿次(非常昂贵的计算),所以每个节省的计算工作都很好。并使用" frexp"将尾数作为double返回不合适。

我的问题是(对于带有IEEE 754浮点的c ++编译器):

1)如何读取float / double的尾数的特定位?

2)如何将整个尾数读入float / double的整数/字节?

3)与指数的1),2)相同的问题。

4)与写作1),2),3)相同的问题。

如果我直接使用尾数或指数,我的动机是更快的计算。我想必须有一个非常简单的解决方案。

3 个答案:

答案 0 :(得分:6)

  

在许多情况下,直接使用尾数和指数有明显更好的方法。

我知道从我的信号处理工作中感觉很好,但事实是,指数和尾数不仅仅是单独的数字; IEEE754规定了一些特殊情况和偏移等。

  

我认为必须有一个非常简单的解决方案。

工程经验告诉我:以“简单解决方案”结尾的句子通常都不正确。

  

“学术案例”

然而,绝对不是真的(我最后会提到一个例子)。

IEEE754浮点数的优化实际使用非常可靠。但是,我发现,后来x86处理器能够执行SIMD(单指令,多数据)以及浮点与大多数“位移”操作一样快的总体事实,我通常怀疑你是不明智的尝试自己做一点点。

通常,由于IEEE754是标准,因此您可以找到有关它如何存储在您的特定架构中的文档。如果你看过,你至少应该找到维基百科文章解释如何做1)和2)(它不像你想象的那样静态)。

更重要的是: 尝试比编译器更聪明。你可能不会,除非你明确地知道如何对多个相同的操作进行矢量化。

尝试使用特定编译器的数学优化。如上所述,现在它们通常做得不多;执行浮点计算的CPU并不比整数运算慢。

我宁愿看看你的算法并寻找优化的潜力。

另外,当我在它的时候,让我们主要讲述VOLK(矢量优化内核库),这是一个用于信号处理的数学库。 http://libvolk.org有一个概述。查看以32f开头的kernels,例如32f_expfast。您会注意到有不同的实现,通用和CPU优​​化的实现,每个SIMD指令集都不同。

答案 1 :(得分:1)

您可以将fp值的地址复制到unsigned char*,并将结果指针视为覆盖fp值的数组的地址。

答案 2 :(得分:-1)

在C或C ++中,如果x是IEEE双精度,那么如果L是64位长整数,那么表达式

L = *((long *) &x);

将允许直接访问位。 如果s是表示符号的字节(0 ='+',1 =' - '),则e是表示无偏指数的整数,f表示长整数小数位然后

s = (byte)(L >> 63);

e = ((int)(L >> 52) & 0x7FF) - 0x3FF;

f = (L & 0x000FFFFFFFFFFFFF);

(如果f是一个归一化的数字,即不是0,非正规,inf,也不是NaN,那么最后一个表达式应该加0x0010000000000000以允许IEEE双精度中隐含的高阶1位格式。)

将符号,指数和分数重新打包成双重类似:

L =(s <&lt; 63)+((e + 0x3FF)&lt;&lt; 52)+(f&amp; 0x000FFFFFFFFFFFFF);

x = *((double *)&amp; L);

上面的代码只生成一些机器指令,在使用64位代码编译的64位机器上没有子程序调用。对于32位代码,有时会调用64位算术,但好的编译器通常会生成内联代码。在任何一种情况下,这种方法都非常快。

类似的方法适用于使用L = bitConverter.DoubleToInt64Bits(x);x = BitConverter.Int64BitsToDouble(L);的C#,或者如果允许使用不安全的代码则完全如上所述。