C中的有效浮点模1(浮点的小数部分)



我正在寻找一种非常有CPU效率的方法来计算C中的浮点模一(包括负值(。我正在使用它进行归一化相位缩减(包装,即7.6->0.6、0.2->0.2、-1.1->0.9等(。

据我所知,fmod((和floor((通常效率很低。我不需要函数严格,即考虑nan或inf,因为我负责传递有效值。

我一直在使用

m = x - (float)(int)x;
m += (float)(m<0.f);
// branchless form to add one if m is negative but not zero

从基准测试来看,它通常比fmod((或使用floor((代替int强制转换效率高得多,但我想知道是否存在一种更有效的方法,也许是基于比特操作。。。

我使用gcc在64位intel cpu上进行编码,但出于我的目的,我使用32位单精度浮点。

如果在其他地方也有同样的问题,我很抱歉,但从我的搜索中,我找不到任何关于这个特定主题的信息。

编辑:很抱歉,我意识到在最初发布的代码中有一个微妙的错误,所以我必须修复它。如果结果(m(为负,则必须添加1,而不是如果x为负

编辑2:事实上,在GCC 12上使用x-floor(x(而不是x-(float((int(x对同一函数进行基准测试并启用所有数学优化后,我必须说前者更快,因为GCC显然足够聪明,可以用非常高效的代码替换floor((内联函数(至少在我的intel i7上是这样(。然而,并非每个cpu和编译器都是这样,因为在其他情况下,根据个人经验,floor((和fmod((的效率都非常低。因此,我对比特操作或类似技巧的追求可能会更快,并且每种编译器和体系结构仍然适用

C++中的一个原型(我不是最新的C语言(,填充逻辑仍然没有优化,但如果你的系统上有AVX512,你可以这样做,在一个循环中处理8个双精度或16个浮点。我在这里发现了很多有用的东西:内部技巧备忘单

我使用了Visual Studio 2022 中的MSVC编译器

#include <type_traits>
#include <vector>
#include <immintrin.h>

void reduce_phases(std::vector<double>& inputs)
{
static constexpr std::size_t vector_size = 512ul / sizeof(double);
auto number_to_pad = vector_size - (inputs.size() % vector_size);
inputs.insert(inputs.end(), number_to_pad, 0.0);
auto data_ptr = inputs.data();

for (std::size_t n{ 0ul }; n < inputs.size(); n += vector_size, data_ptr += vector_size)
{
auto values = _mm512_load_pd(data_ptr);
auto floors = _mm512_floor_pd(values);
auto result = _mm512_sub_pd(values, floors);
_mm512_store_pd(data_ptr, result);
}
inputs.erase(inputs.end() - number_to_pad, inputs.end());
}
void reduce_phases(std::vector<float>& inputs)
{
static constexpr std::size_t vector_size = 512ul / sizeof(float);
auto number_to_pad = vector_size - (inputs.size() % vector_size);
inputs.insert(inputs.end(), number_to_pad, 0.0);
auto data_ptr = inputs.data();
for (std::size_t n{ 0ul }; n < inputs.size(); n += vector_size, data_ptr += vector_size)
{
auto values = _mm512_load_ps(data_ptr);
auto floors = _mm512_floor_ps(values);
auto result = _mm512_sub_ps(values, floors);
_mm512_store_ps(data_ptr, result);
}
inputs.erase(inputs.end() - number_to_pad, inputs.end());
}

int main()
{
std::vector<double> values{ -1.1, -1.9, -1.5, -0.4, 0.0, 0.4, 1.5, 1.9, 2.1 };
reduce_phases(values);
std::vector<float> float_values{ -1.1f, -1.9f, -1.5f, -0.4f, 0.0f, 0.4f, 1.5f, 1.9f, 2.1f };
reduce_phases(float_values);
return 0;
}

最新更新