如果我想将一个无符号整数四舍五入到最接近的更小或等于偶数的整数,我可以除以2然后乘以2吗
If I want to round an unsigned integer to the closest smaller or equal even integer, can I divide by 2 then multiply by 2?
例如:
f(8)=8
f(9)=8
我可以做x = x/2*2;
吗?编译器会优化掉这样的表达式吗?
编译器可以进行任何它喜欢的优化,只要它不会给程序带来任何副作用。在您的情况下,它无法取消"2",因为表达式将具有不同的奇数值。
x / 2 * 2
被严格地评估为(x / 2) * 2
,如果x
是积分类型,则x / 2
在整数运算中执行。
事实上,这是一种惯用的四舍五入技巧。
由于指定整数是无符号的,因此可以使用一个简单的掩码:
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)
C和C++在优化中通常使用"好像"规则。计算结果必须是,就好像编译器没有优化代码一样。
在这种情况下,9/2*2=8
。编译器可以使用任何方法来实现结果8。这包括比特掩码、比特移位或任何产生相同结果的CPU特定破解(x86有很多技巧,它们依赖于它不区分指针和整数,这与C和C++不同)。
如果x
具有无符号类型,则可以编写x / 2 * 2
,编译器将生成非常有效的代码来清除最低有效位。
相反,你可以写:
x = x & ~1;
或者可能不太可读:
x = x & -2;
甚至
x = (x >> 1) << 1;
或者这个:
x = x - (x & 1);
或者最后一个,由supercat提出,适用于所有整数类型和表示的正值:
x = (x | 1) ^ 1;
以上所有建议都适用于2的补码体系结构上的所有无符号整数类型。编译器是否会生成最佳代码是配置和实现质量的问题。
然而,请注意,如果x
的类型大于unsigned int
,则x & (~1u)
不起作用。这是一个反直觉的陷阱。如果坚持使用无符号常量,则必须写入x & ~(uintmax_t)1
,因为如果x
的类型大于unsigned long long
,则即使是x & ~1ULL
也会失败。更糟糕的是,许多平台现在都有大于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的编译器资源管理器上测试表明,对于上述所有备选方案,gcc -O1
为unsigned int
生成完全相同的代码,但将类型T
更改为unsigned long long
会为test1u
生成不同的代码。
如果您的值如您所说是任何无符号类型,那么最简单的是
x & -2;
无符号算术的奇妙之处在于,-2
被转换为x
类型,并且具有除最低有效位0
之外的所有1的比特模式。
与其他一些提出的解决方案相反,这应该适用于任何宽度至少与unsigned
一样宽的无符号整数类型。(无论如何,你不应该对较窄的类型进行算术运算。)
额外的好处,正如supercat所说,这只使用有符号类型到无符号类型的转换。这被标准定义为模运算。所以对于UTYPE
,结果总是UTYPE_MAX-1
——x
的无符号类型。特别是,它独立于平台的签名类型的签名表示。
我很惊讶到目前为止还没有提到的一个选项是使用模运算符。我认为这至少和你的原始片段一样代表了你的意图,甚至可能更好。
x = x - x % 2
正如其他人所说,编译器的优化器将等效地处理任何合理的表达式,所以要担心什么更清楚,而不是你认为什么最快。所有的小调整答案都很有趣,但你绝对不应该用它们来代替算术运算符(假设动机是算术而不是小调整)。
只需使用以下内容:
template<class T>
inline T f(T v)
{
return v & (~static_cast<T>(1));
}
不要害怕这是函数,编译器最终应该将其优化为v&(~1)其中适当的类型为1。
- 我可以信任表示整数的浮点或双精度来保持精度吗
- 我可以将迭代器递增一个整数吗?
- 在C++中,如果我可以直接将整数分配给指针而不使用"new",为什么要使用"new"?
- 我无法显示包含整数最大值的索引. 但我可以显示数组中整数的最高值
- 我可以将比较结果用作C++的整数吗?
- 我可以使用什么方法或方式将用户输入的字符串转换为 C++ 中的整数
- 如果我想将一个无符号整数四舍五入到最接近的更小或等于偶数的整数,我可以除以2然后乘以2吗
- 我可以使用STD ::复制将数据的位模式从整数向量复制到一系列未签名的字符
- 我可以将指针地址(即十六进制整数)转换为C 中的十进制和八分位基础
- C++ 我可以使用字符串::at分配一个整数吗?
- 我可以使用有符号整数作为 __builtin_popcount() 的参数吗?
- 我可以在 c++ 中使用整数指针调用类的成员函数吗?
- 我可以在printf中需要整数的地方传递一个字符吗
- 我如何使用关系 btwn 3 整数来确定三角形和/或我可以使用什么声明来使这个程序工作
- 我可以在C++中制作一个强类型整数吗?
- 我可以把mmap的结果存储在一个整数中吗
- typedef int -> 整数。我可以在 c++ 中执行 cout-> 打印吗?
- 我可以在C/ c++中指定是否封顶或溢出整数吗?
- 我可以检查输入的值是字符串还是整数在c++
- 我可以做一个双倍和与一个整数