我使用c ++来计算各种类型的特殊函数(例如Lambert函数,用于评估反演的迭代方法等)。在许多情况下,直接使用尾数和指数有明显更好的方法。
我找到了许多答案,如何提取尾数和指数部分,但是所有这些都只是"具有不太有效的计算速度的学术案例"对我来说有点无用(我用尾数和指数操作的动机是提高计算速度)。有时我需要调用一些特定功能大约十亿次(非常昂贵的计算),所以每个节省的计算工作都很好。并使用" frexp"将尾数作为double返回不合适。
我的问题是(对于带有IEEE 754浮点的c ++编译器):
1)如何读取float / double的尾数的特定位?
2)如何将整个尾数读入float / double的整数/字节?
3)与指数的1),2)相同的问题。
4)与写作1),2),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#,或者如果允许使用不安全的代码则完全如上所述。