如果我想将无符号整数舍入到最接近的较小或相等的偶数整数,我可以除以2然后乘以2吗?

时间:2017-10-18 07:49:02

标签: c++ c

例如:

f(8)=8
f(9)=8

我可以x = x/2*2;吗? 是否存在编译器优化此类表达式的风险?

7 个答案:

答案 0 :(得分:87)

只要不对程序引入任何副作用,编译器就可以进行任何喜欢的优化。在你的情况下,它不能取消'2',因为表达式将具有不同的奇数值。

x / 2 * 2严格评估为(x / 2) * 2,如果x / 2是整数类型,则x将以整数算术执行。

事实上,这是一种惯用的舍入技术。

答案 1 :(得分:54)

由于您指定的整数是无符号的,因此您可以使用简单的掩码:

x & (~1u)

将LSB设置为零,从而产生不大于x的立即偶数。也就是说,如果x的类型不比unsigned int宽。

您当然可以强制1与更宽x的类型相同,如下所示:

x & ~((x & 1u) | 1u)

但是在这一点上,你真的应该把这种方法视为混淆练习,并接受芭丝谢芭的答案。

我当然忘记了标准库。如果包含stdint.h(或cstdint,则应该包含在C ++代码中。您可以让实现处理细节:

uintmax_t const lsb = 1;
x & ~lsb;

x & ~UINTMAX_C(1)

答案 2 :(得分:38)

C和C ++通常在优化中使用“as if”规则。计算结果必须,好像编译器没有优化您的代码。

在这种情况下,9/2*2=8。编译器可以使用任何方法来实现结果8.这包括位掩码,位移或任何产生相同结果的特定于CPU的hack(x86有很多技巧依赖于它不区分指针的事实)和整数,不像C和C ++)。

答案 3 :(得分:21)

您可以编写x / 2 * 2,如果x具有无符号类型,编译器将生成非常有效的代码以清除最低有效位。

相反,你可以写:

x = x & ~1;

或者可能不太可读:

x = x & -2;

甚至

x = (x >> 1) << 1;

或者这也是:

x = x - (x & 1);

或者最后一个,由supercat建议,适用于所有整数类型和表示的正值:

x = (x | 1) ^ 1;

以上所有提议都适用于2的补码架构中的所有无符号整数类型。编译器是否会产生最佳代码是一个配置和实现质量的问题。

但请注意,如果x & (~1u)的类型大于x,则unsigned int不起作用。这是一个反直觉的陷阱。如果您坚持使用无符号常量,则必须写x & ~(uintmax_t)1,因为如果x & ~1ULL的类型大于x,则unsigned long long会失败。更糟糕的是,许多平台现在都有大于uintmax_t的整数类型,例如__uint128_t

这是一个小基准:

typedef unsigned int T;

T test1(T x) {
    return x / 2 * 2;
}

T test2(T x) {
    return x & ~1;
}

T test3(T x) {
    return x & -2;
}

T test4(T x) {
    return (x >> 1) << 1;
}

T test5(T x) {
    return x - (x & 1);
}

T test6(T x) {  // suggested by supercat
    return (x | 1) ^ 1;
}

T test7(T x) {  // suggested by Mehrdad
    return ~(~x | 1);
}

T test1u(T x) {
    return x & ~1u;
}

根据Ruslan的建议,对Godbolt's Compiler Explorer进行测试表明,对于上述所有选项gcc -O1unsigned int生成相同的确切代码,但将类型T更改为{ {1}}显示unsigned long long的不同代码。

答案 4 :(得分:7)

如果你的值是你所说的任何无符号类型,最简单的是

x & -2;

无符号算术的奇迹使-2转换为x的类型,并且具有包含所有1的位模式,但对于最低有效位{{1} }。

与其他一些提议的解决方案相反,这应该适用于任何至少与0一样宽的无符号整数类型。 (无论如何,你不应该用较窄的类型做算术。)

额外奖励,正如supercat所说,它只使用有符号类型转换为无符号类型。标准将其定义为模运算。因此unsigned的结果始终是UTYPE_MAX-1无符号类型UTYPE。 特别是,它与签名类型的平台的符号表示无关。

答案 5 :(得分:6)

到目前为止,我很惊讶的一个选项是使用模运算符。我认为这至少和你原来的片段一样代表你的意图,甚至可能更好。

x = x - x % 2

正如其他人所说,编译器的优化器会等同地处理任何合理的表达式,所以要担心什么更清楚,而不是你认为最快的。所有的位调整答案都很有趣,但你绝对不应该使用它们来代替算术运算符(假设动机是算术而不是位调整)。

答案 6 :(得分:-2)

只需使用以下内容:

template<class T>
inline T f(T v)
{
    return v & (~static_cast<T>(1));
}

不要害怕这是函数,编译器最终应该优化它     v&amp; (〜1) 适当的类型为1。