关于C中的位屏蔽。为什么(~(~0 << N))比((1 << N) -1)更好?

发布于 2024-12-08 15:55:16 字数 143 浏览 1 评论 0 原文

我确实知道 ~0 将评估最大字大小的位 1(因此需要考虑可移植性),但我仍然不明白为什么 ((1 << N) - 1) 灰心丧气?

如果您使用第二种形式并遇到任何麻烦,请分享。

I do know that ~0 will evaluate the maximum word sized bit 1s (and thus takes caring of portability), but I am still not getting why ((1 << N) - 1) is discouraged?

Please share if you used the second form and got into any trouble.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

黑白记忆 2024-12-15 15:55:16

查看这些行:

1. printf("%X", ~(~0 << 31) );
2. printf("%X", (1 << 31) - 1 );

1 行编译并表现得与预期一致。

2 行给出警告表达式中的整数溢出

这是因为 1 << 31 默认情况下被视为有符号 int,因此 1 << 31 = -2147483648,这是可能的最小整数。

因此,休息 1 会导致溢出。

Look at these lines:

1. printf("%X", ~(~0 << 31) );
2. printf("%X", (1 << 31) - 1 );

Line 1 compiles and behaves like expected.

Line 2 gives the warning integer overflow in expression.

This is because 1 << 31 is treated by default as a signed int, so 1 << 31 = -2147483648, which is the smallest possible integer.

As a result, resting 1 causes an overflow.

断桥再见 2024-12-15 15:55:16

第一种形式绝对不是首选,我什至会说它应该永远被使用。在不支持负零的补码系统上,~0 很可能是一个陷阱表示,因此在使用时会调用 UB。

另一方面,假设 int 是 32 位,1<<31 也是 UB,因为它会溢出。

如果您确实将 31 视为常量,则 0x7fffffff 是编写掩码的最简单且最正确的方法。如果您想要除 int 的符号位之外的所有内容,INT_MAX 是编写掩码的最简单且最正确的方法。

只要您知道位移不会溢出,(1< 就是使用最低 n 位集制作掩码的正确方法。最好使用 (1ULL< ,然后进行强制转换或隐式转换,以便不必担心符号问题和移位中的溢出。

但无论您做什么,都不要将 ~ 运算符与有符号整数一起使用。曾经。

The first form is definitely not preferred, and I would go so far as to say it should never be used. On a ones complement system that does not support negative zero, ~0 may very well be a trap representation and thus invoke UB when used.

On the other hand, 1<<31 is also UB, assuming int is 32-bit, since it overflows.

If you really mean 31 as a constant, 0x7fffffff is the simplest and most correct way to write your mask. If you want all but the sign bit of an int, INT_MAX is the simplest and most correct way to write your mask.

As long as you know the bitshift will not overflow, (1<<n)-1 is the correct way to make a mask with the lowest n bits set. It may be preferable to use (1ULL<<n)-1 followed by a cast or implicit conversion in order not to have to worry about signedness issues and overflow in the shift.

But whatever you do, don't use the ~ operator with signed integers. Ever.

↙温凉少女 2024-12-15 15:55:16

我不鼓励这两种方法,对有符号值进行移位或求补操作根本就是一个坏主意。位模式应始终在无符号类型上生成,然后(如果有必要)转换为有符号计数器部分。那么使用基本类型也不是一个好主意,因为通常在位模式上,您应该控制正在处理的位数。

所以我总是会做一些

-UINT32_C(1)
~UINT32_C(0)

完全等价的事情,最后只是为了使用 UINT32_MAX 和 Co。

只有在你不完全转换的情况下才需要转换,比如

(UINT32_C(1) << N) - UINT32_C(1)

I would discourage both, shift or complement operations on signed values is simply a bad idea. Bit patterns should always be produced on unsigned types and (if even necessary) then transposed to the signed counter parts. Then using the primitive types is also no so good as an idea because usually on bit patterns you should control the the number of bits that you are handling.

So I'd always do something like

-UINT32_C(1)
~UINT32_C(0)

which are completely equivalent and at the end this comes just to use UINT32_MAX and Co.

Shift is only necessary in cases you don't shift fully, something like

(UINT32_C(1) << N) - UINT32_C(1)
§普罗旺斯的薰衣草 2024-12-15 15:55:16

我不喜欢其中一个,但我见过许多 (1< 的错误,其中值必须是 64 位,但“1”是 32 位(整数是32 位),当 N>=31 时,结果是错误的。 1ULL 而不是 1 可以修复它。这是这种转变的危险之一。

此外,未定义将 int 移位 CHAR_BIT*sizeof(int) 或更多位置(对于 long long(通常为 64 位)按 CHAR_BIT*sizeof(long long) 或更多位置类似)。因此,像这样右移可能更安全:~0u>>(CHAR_BIT*sizeof(int)-N),但在这种情况下,N 不能为 0。

I would not prefer one to another, but I've seen many bugs with (1<<N) where the value had to be 64-bit but "1" was 32-bit (ints were 32-bit) and the result was wrong for N>=31. 1ULL instead of 1 would fix it. That's one danger of such shifts.

Also, shifts of ints by CHAR_BIT*sizeof(int) or more positions (similarly for long long's (which are often 64-bit) by CHAR_BIT*sizeof(long long) or more positions) aren't defined. Because of that it may be safer to shift right like this: ~0u>>(CHAR_BIT*sizeof(int)-N), but in this case N can't be 0.

递刀给你 2024-12-15 15:55:16

编辑:纠正了一个愚蠢的错误;并指出可能的溢出问题。

我从未听说过一种形式优于另一种形式。两种形式都在编译时进行评估。我总是使用第二种形式,而且从未遇到过任何麻烦。这两种形式对读者来说都是非常清楚的。

其他答案指出了第二种形式溢出的可能性。

我认为他们之间几乎没有什么选择。

EDIT: corrected a stupid error; and noted possible overflow problems.

I have never heard that one form is preferred over the other. Both forms are evaluated at compile time. I always use the second form, and I've never gotten into any trouble. Both forms are perfectly clear to the reader.

Other answers noted the possibility of overflow in the second form.

I see little to choose between them.

蹲墙角沉默 2024-12-15 15:55:16

为什么不鼓励
~0 是单周期操作,因此速度更快
((1<首先进行移位,然后然后进行减法,这是一种算术运算。因此由于减法将消耗大量周期,因此产生不必要的开销。

更多
更重要的是,当你这样做时 ((1 << N)-1) 或 ((M << N)-1) 是相同的,假设 N 指的是 M 的位大小,因为它将刷新所有位。这里 1 是整数,在几乎所有现有平台 32/64 位上通常为 32 位,因此 N 可以假设为 32。

但是,如果将 1 类型转换为 long 并执行 (((long)1 << 32)-1)。
这里你需要使用 64 代替 32,64 是 long 的位大小。

Why Discouraged
~0 is a single cycle operation and hence faster
((1<first do a shift and then a subtraction which is an arithmetic operation. so due to subtraction it will consume a lot of cycles and hence unnecessary overhead.

More
more over, when you do ((1 << N)-1) or ((M << N)-1) is same, assuming N refers to M's size in bits because it will flush all the bits. here 1 is integer, typically 32 bit on almost all the present platforms 32/64 bit, so N can be assumed 32.

The result will however not same if you typecast 1 to long and do (((long)1 << 32) -1).
here you need to use 64 in place of 32, 64 being the size of long in bits.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文