对于我正在处理的应用程序,我需要取两个整数,并使用特定的数学公式将它们加在一起。最终看起来像这样:
int16_t add_special(int16_t a, int16_t b) {
float limit = std::numeric_limits<int16_t>::max();//32767 as a floating point value
float a_fl = a, b_fl = b;
float numerator = a_fl + b_fl;
float denominator = 1 + a_fl * b_fl / std::pow(limit, 2);
float final_value = numerator / denominator;
return static_cast<int16_t>(std::round(final_value));
}
任何对物理学有一定了解的读者都会认识到,该公式与用于计算近光速速度总和的公式相同,此处的计算有意反映该计算。
编写的代码给出了我需要的结果:对于低数字,它们通常 加在一起,但是对于高数字,它们收敛到最大值32767,即
add_special(10, 15) == 25
add_special(100, 200) == 300
add_special(1000, 3000) == 3989
add_special(10000, 25000) == 28390
add_special(30000, 30000) == 32640
所有这些似乎都是正确的。
但是,问题在于编写的函数涉及先将数字转换为浮点值,然后再将其转换回整数。对于我所知道的数字,这似乎是不必要的绕道,作为其域的原理,它永远不会是整数。
我正在使用MSVC 14.X为x86-64进行构建,尽管也适用于GCC的方法将是有益的。另外,我对现阶段的SSE / SIMD优化不感兴趣。我主要只是查看对数据执行的基本操作。
答案 0 :(得分:2)
您可能会避免使用浮点数,并以整数类型进行所有计算:
constexpr int16_t add_special(int16_t a, int16_t b) {
std::int64_t limit = std::numeric_limits<int16_t>::max();
std::int64_t a_fl = a;
std::int64_t b_fl = b;
return static_cast<int16_t>(((limit * limit) * (a_fl + b_fl)
+ ((limit * limit + a_fl * b_fl) / 2)) /* Handle round */
/ (limit * limit + a_fl * b_fl));
}
但是根据Benchmark来说,这些值并不更快。
答案 1 :(得分:1)
建议:
32767.0*32767.0
(这是一个常量)而不是std::pow(limit, 2)
。inline
。类似的东西:
int16_t add_special(int16_t a, int16_t b) {
float numerator = int32_t(a) + int32_t(b); // Cannot overflow.
float denominator = 1 + (int32_t(a) * int32_t(b)) / (32767.0 * 32767.0); // Cannot overflow either.
return (numerator / denominator) + 0.5; // Relying on implementation defined rounding. Not good but potentially faster than std::round().
}
上述唯一的风险是省略了显式舍入,因此您将获得一些隐式舍入。
答案 2 :(得分:1)
Johannes Overmann指出,通过避免使用std::round
可以大大提高性能,但是结果却有些(很小)差异。
我尝试了其他一些小的更改HERE,似乎以下是一种更快的方法(至少对于该体系结构而言)
constexpr int32_t i_max = std::numeric_limits<int16_t>::max();
constexpr int64_t i_max_2 = static_cast<int64_t>(i_max) * i_max;
int16_t my_add_special(int16_t a, int16_t b)
{
// integer multipication instead of floating point division
double numerator = (a + b) * i_max_2;
double denominator = i_max_2 + a * b;
// Approximated rounding instead of std::round
return 0.5 + numerator / denominator;
}