在 C++ 中四舍五入到最近的偶数
Rounding to Nearest Even Number in C++
我想知道 C++ 是否包含一种舍入到最接近的偶数的方法。我环顾四周,似乎找不到关于该主题的任何内容。我可以编写自己的方法,但使用内置方法很可能会更快。
谢谢。
没有内置函数可以执行此操作。最直接的方法可能是做类似的事情:
even = round(x / 2) * 2;
如前所述,没有内置方法。因此,让我们看看有几种方法可以做到这一点。但首先...
性能注意事项:
- 避免除法,因为它对浮点数来说仍然很慢,尽管不像对整数那样糟糕。对于浮点数,乘法几乎和加法一样快。
- 乘以 2 -x 对于任何整数 x 保证等于
除以 2 x,同时不损失精度。
- 将所有常量设置为同一类型以防止提升,尤其是当值将再次以同一类型存储时。
简单
std::round( x * 0.5f ) * 2.0f
这将在每个奇数处从零舍入,从而产生小偏差。
一般来说,尽可能避免std::round
。它很慢,因为它必须:保存舍入模式,切换它,舍入,然后恢复以前的舍入模式。所有这些都是为了引入偏见。
幻数
在 C++11 之前,我们可以使用幻数通过使用内置的银行家舍入来减轻偏差。 警告: 这依赖于默认的舍入模式 (FE_TONEAREST
),通常 99.9999% 的时间都是这样,但要小心其他库可能会改变它而不是之后恢复它。
float
魔法:( x + 25165824f ) - 25165824f
这利用了浮点精度只有那么多位的事实。通过添加一个足够大的幻数,我们不需要的最低位将自动舍入,之后我们减去我们的幻数,使我们的原始四舍五入到正确的精度。
幻数计算为 1.5 * 2 p 其中 p 是精度浮点数有效位数:float
为 24,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 将创建标准银行家四舍五入。
C++11
所有人都欢呼新的四舍五入方式:std::nearbyint
. This obeys the current rounding mode, so if the rounding mode's been twiddled with by something else, we can use std::fesetround
将其恢复为 FE_TONEAREST
。
std::nearbyint( x * 0.5f ) * 2.0f
这与第一个版本一样直观,但没有任何偏见。
我想知道 C++ 是否包含一种舍入到最接近的偶数的方法。我环顾四周,似乎找不到关于该主题的任何内容。我可以编写自己的方法,但使用内置方法很可能会更快。
谢谢。
没有内置函数可以执行此操作。最直接的方法可能是做类似的事情:
even = round(x / 2) * 2;
如前所述,没有内置方法。因此,让我们看看有几种方法可以做到这一点。但首先...
性能注意事项:
- 避免除法,因为它对浮点数来说仍然很慢,尽管不像对整数那样糟糕。对于浮点数,乘法几乎和加法一样快。
- 乘以 2 -x 对于任何整数 x 保证等于 除以 2 x,同时不损失精度。
- 将所有常量设置为同一类型以防止提升,尤其是当值将再次以同一类型存储时。
简单
std::round( x * 0.5f ) * 2.0f
这将在每个奇数处从零舍入,从而产生小偏差。
一般来说,尽可能避免std::round
。它很慢,因为它必须:保存舍入模式,切换它,舍入,然后恢复以前的舍入模式。所有这些都是为了引入偏见。
幻数
在 C++11 之前,我们可以使用幻数通过使用内置的银行家舍入来减轻偏差。 警告: 这依赖于默认的舍入模式 (FE_TONEAREST
),通常 99.9999% 的时间都是这样,但要小心其他库可能会改变它而不是之后恢复它。
float
魔法:( x + 25165824f ) - 25165824f
这利用了浮点精度只有那么多位的事实。通过添加一个足够大的幻数,我们不需要的最低位将自动舍入,之后我们减去我们的幻数,使我们的原始四舍五入到正确的精度。
幻数计算为 1.5 * 2 p 其中 p 是精度浮点数有效位数:float
为 24,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 将创建标准银行家四舍五入。
C++11
所有人都欢呼新的四舍五入方式:std::nearbyint
. This obeys the current rounding mode, so if the rounding mode's been twiddled with by something else, we can use std::fesetround
将其恢复为 FE_TONEAREST
。
std::nearbyint( x * 0.5f ) * 2.0f
这与第一个版本一样直观,但没有任何偏见。