C++ 如何强制pow(float,int)返回float
重载函数C++ 如何强制pow(float,int)返回float,c++,c++11,pow,C++,C++11,Pow,重载函数float-pow(float-base,int-iexp)在C++11中被删除,现在pow返回一个double。在我的程序中,我计算了很多(单精度),我对最有效的方法很感兴趣 是否有具有上述签名的特殊功能(在标准库或任何其他库中) 如果不是,那么(就单精度性能而言)在任何其他操作之前将pow的结果显式转换为float(这会将所有其他操作转换为double)或将iexp转换为float并使用重载函数float-pow(float-base,float-exp) 编辑:为什么我需要floa
float-pow(float-base,int-iexp)
在C++11中被删除,现在pow
返回一个double
。在我的程序中,我计算了很多(单精度),我对最有效的方法很感兴趣
是否有具有上述签名的特殊功能(在标准库或任何其他库中)
如果不是,那么(就单精度性能而言)在任何其他操作之前将pow
的结果显式转换为float
(这会将所有其他操作转换为double
)或将iexp
转换为float
并使用重载函数float-pow(float-base,float-exp)
编辑:为什么我需要float
而不使用double
?
主要原因是RAM——我需要数十或数百GB的内存,所以这种减少是巨大的优势。所以我需要从
float
获取float
。现在我需要最有效的方法来实现这一点(减少强制转换,使用已经优化过的算法等等)。尝试使用powf()。这是C99函数,在C++11中也应该可用。您可以使用它轻松编写自己的fpow
钻孔部分: 当| base |>1时,此算法提供了最佳精度,可以使用
float
类型存档
证明:
让我们来计算pow(a,n)
其中a
是基,n
是指数。让我们定义b1=a1、b2=a2、b3=a4、b4=a8等等。
然后an是所有bi的乘积,其中第i位设置在n中 因此,我们对集合B={bk1,bk1,…,bkn}进行了排序,对于任何j,位kj都是在n中设置的 以下明显的算法A可用于舍入误差最小化:
- 如果B包含单个元素,则它是结果
- 用最小模从B中选取两个元素p和q
- 从B中移除它们
- 计算产品s=p*q并将其放入B
- 走第一步
然后,表达式
result*=base
(当然,除了第一次迭代之外)将B中的两个最小数相乘,因此舍入误差最小。因此,代码使用了算法A另一个问题,只能用“错误的问题”诚实地回答。或者至少:“你真的愿意去那里吗?”float
理论上需要减少约80%的芯片空间(对于相同的周期数),因此批量处理成本更低。GPU之所以喜欢浮动就是因为这个原因
但是,让我们看看x86(无可否认,您没有说明您使用的是什么体系结构,所以我选择了最常见的体系结构)。模具领域的价格已经支付。通过使用float
进行计算,您实际上一无所获。实际上,您甚至可能会失去吞吐量,因为需要从float
到double
的额外扩展,以及到中间float
精度的额外舍入。换言之,你需要付出额外的代价才能得到不太准确的结果。这通常是要避免的,除非您需要与其他程序最大限度地兼容
参见Jens的评论。这些选项允许编译器忽略某些语言规则以获得更高的性能。不用说,这有时会适得其反
在x86上,有两种情况下,float
可能更有效:
- GPU(包括GPGPU),事实上,许多GPU甚至不支持双精度,如果支持双精度,通常速度要慢得多。然而,只有在进行大量此类计算时,您才会注意到
- CPU SIMD aka矢量化
向量中的每个数字乘以另一个浮点数,这种情况在IMO中并不明显。即使将向量中的每个数乘以相同的int
,编译器可能不够聪明,无法有效地将其矢量化,特别是当pow
驻留在另一个翻译单元中,并且没有有效的链接时代码生成时
如果你还没有准备好改变你的程序的整个结构,以允许有效地使用SIMD(包括GPGPU),而你不是在一个架构中,<代码>浮点< /代码>在默认情况下确实便宜得多,我建议你一定要遵守<代码>双< /代码>,并考虑<代码>浮点最适合保存RAM的存储格式,或者提高缓存局部性(当您有很多)时。即使如此,测量也是一个很好的主意
也就是说,您可以尝试ivaigult的算法(仅对中间值和结果使用double
),该算法与一个名为(以及各种其他名称)的经典算法相关,只是操作数是相乘的,而不是相加的。我不知道pow(double,double)
是如何精确工作的,但可以想象,在某些情况下,该算法可能会更快。同样,你也应该对基准测试有所了解
t是否有一些特殊功能(在标准库或任何其他库中)
float my_fpow(float base, unsigned exp)
{
float result = 1.f;
while (exp)
{
if (exp & 1)
result *= base;
exp >>= 1;
base *= base;
}
return result;
}
#include <iostream>
#include <boost/timer/timer.hpp>
#include <boost/random/mersenne_twister.hpp>
#include <boost/random/uniform_real_distribution.hpp>
#include <cmath>
int main ()
{
boost::random::mt19937 gen;
boost::random::uniform_real_distribution<> dist(0, 10000000);
const size_t size = 10000000;
std::vector<float> bases(size);
std::vector<float> fexp(size);
std::vector<int> iexp(size);
std::vector<float> res(size);
for(size_t i=0; i<size; i++)
{
bases[i] = dist(gen);
iexp[i] = std::floor(dist(gen));
fexp[i] = iexp[i];
}
std::cout << "float pow(float, int):" << std::endl;
{
boost::timer::auto_cpu_timer timer;
for(size_t i=0; i<size; i++)
res[i] = std::pow(bases[i], iexp[i]);
}
std::cout << "float pow(float, float):" << std::endl;
{
boost::timer::auto_cpu_timer timer;
for(size_t i=0; i<size; i++)
res[i] = std::pow(bases[i], fexp[i]);
}
return 0;
}
float __builtin_powif(float, int)