Vit*_*meo 3 c++ math floating-point optimization branchless
我想实现一个符合以下接口和契约的函数:
void move_towards(float& value, float target, float step)
// Moves `value` towards `target` by `step`.
// `value` will never go beyond `target`, but can match it.
// If `step == 0.0f`, `value` is unchanged.
// If `step > 0.0f`, `std::abs(target - value)` decreases.
// If `step < 0.0f`, the behavior is undefined.
Run Code Online (Sandbox Code Playgroud)
其想法是使用此函数逐渐将现有浮点值移向另一个浮点值,而不会超过目标。例如,这对于在值之间执行线性转换作为游戏循环执行的一部分非常有用。
这是一个测试用例示例:
float value = 5.0f;
move_towards(value, 10.f, 1.f); assert(value == 6.0f);
move_towards(value, 10.f, 1.f); assert(value == 7.0f);
move_towards(value, -5.f, 5.f); assert(value == 2.0f);
move_towards(value, -5.f, 5.f); assert(value == -3.0f);
move_towards(value, -5.f, 5.f); assert(value == -5.0f);
move_towards(value, -5.f, 5.f); assert(value == -5.0f);
move_towards(value, 0.f, 15.f); assert(value == 0.0f);
Run Code Online (Sandbox Code Playgroud)
std::copysign我使用和的组合尝试了一些无分支的想法std::clamp,但它们在某些边缘情况下总是失败。最后,我求助于使用分支版本:
void move_towards(float& value, float target, float step)
{
if (value < target)
{
value += step;
if (value > target)
{
value = target;
}
}
else if (value > target)
{
value -= step;
if (value < target)
{
value = target;
}
}
}
Run Code Online (Sandbox Code Playgroud)
move_towards以产生无分支指令?所需的结果是value - step、value + step、 和的中位数target,因此有效:
void move_towards(float &value, float target, float step) \n{\n value = std::max(value - step, std::min(value + step, target));\n}\nRun Code Online (Sandbox Code Playgroud)\ntarget要了解这一点,请考虑位于 和 之间、之间或之上value - step的情况value + step:
target\xe2\x89\xa4 value - step< value + step,那么value可以完全step向下趋向target,所以我们想要value - step。value - step< target< value + step,则value不能完全step朝向target(可能在任一方向),所以我们想要target。value - step< value + step\xe2\x89\xa4 target,那么value可以采取一个完整的step向上走向target,所以我们想要value + step。在每种情况下,我们都需要中间值。
\n测试表明,如果我们交换std::max操作数,GCC 会生成更少的两条指令,可能只是因为它与操作数恰好位于寄存器中的位置配合得更好:
void move_towards(float &value, float target, float step) \n{\n value = std::max(std::min(value + step, target), value - step);\n}\nRun Code Online (Sandbox Code Playgroud)\n