The performance implications are insignificant, and every modern compiler should be able to optimize that to avoid the branch in the middle of the loop.
However, this shows an interesting problem, and serves as a great argument against using for loops. They're a very low-level construct and should be avoided where the alternatives can be applied. In this case, using them forces you to choose between a clean-looking implementation (with potential performance drawback) and a technically-better one, but stylistically suboptimal.
Your two operations can be described in terms of two generic operations: copy and transform. You can write the algorithm in terms of them, like so:
if (bitwidth == 0) {
using std::copy_n;
copy_n(res_f32, B * H * W * C, p_res);
} else {
transform_n(res_f32, B * H * W * C, p_res, [=](auto const& x){ return x.toint(bitwidth, fracwidth); });
}
Unfortunately, there's no std::transform_n, but you can make one. Alternatively, and much better, if your B*H*W*C term can be stored in terms of the container, it'd be best to express that as a view or the container's begin/end directly, thus enabling the use of the automatic (non-_n) copy/transform instead.
This is not just more readable, but expresses the logic in the commonly-understood terms of standard operations, leaves less room for errors and allows much easier changes of underlying containers, operations, potential parallelism and overall maintainability.