【发布时间】:2015-07-16 20:42:15
【问题描述】:
我想知道 C++ 是否包含一种四舍五入到最接近偶数的方法。我环顾四周,似乎找不到有关该主题的任何内容。我可以编写自己的方法,但使用内置方法很可能会更快。
谢谢。
【问题讨论】:
-
它是一个浮点值。
我想知道 C++ 是否包含一种四舍五入到最接近偶数的方法。我环顾四周,似乎找不到有关该主题的任何内容。我可以编写自己的方法,但使用内置方法很可能会更快。
谢谢。
【问题讨论】:
没有内置函数可以做到这一点。最直接的方法可能是执行以下操作:
even = round(x / 2) * 2;
【讨论】:
如前所述,没有内置方法。因此,让我们看看可以做到这一点的几种方法。但首先...
性能注意事项:
std::round( x * 0.5f ) * 2.0f
这将在每个奇数处从零四舍五入,从而产生一个小的偏差。
一般情况下,尽可能避免使用std::round。它之所以慢,是因为它必须:保存舍入模式,切换它,舍入,然后恢复之前的舍入模式。所有这些都是为了引入偏见。
在 C++11 之前,我们可以使用内置的银行家四舍五入来使用幻数来缓解偏差。 警告:这依赖于默认的舍入模式 (FE_TONEAREST),它通常是 99.9999% 的时间,但要小心其他库可能会更改它而不是在之后恢复它。
float魔术:( x + 25165824f ) - 25165824f
这使用了浮点精度只有这么多位的事实。通过添加一个足够大的幻数,我们不想要的最低位将自动舍入,然后我们减去我们的幻数,使原始四舍五入到正确的精度。
幻数计算为 1.5 * 2 p 其中 p 是浮点有效数的精度:24 表示 float ,double 为 53,long double 为 64。在std::numeric_limits 的一点帮助下,我们可以使这个通用化。
template< typename T >
T magic_round_to_even( T x ) {
static T magic = 1.5 * std::pow( 2.0L, std::numeric_limits< T >::digits);
return ( x + magic ) - magic;
}
通过将 p 更改为我们的幻数,我们可以四舍五入到 2 的不同幂次。 p - 1 将创建标准的银行家四舍五入。
大家欢迎新的回合方法:std::nearbyint。这符合当前的舍入模式,所以如果舍入模式被其他东西弄乱了,我们可以使用std::fesetround将其恢复为FE_TONEAREST。
std::nearbyint( x * 0.5f ) * 2.0f
这与第一个版本一样直观,但没有任何偏见。
【讨论】:
x / 2.0f 和 x * 0.5f 到同一件事。 gcc.godbolt.org/z/YTm7zu