如何解决按位和超过32位
#include <stdio.h>
#include <limits.h>
int main()
{
unsigned long long a = 9223372036854775808; // a = 2^63
a = a & ~1;
printf("%llu\n",a);
printf("%d,%lld",INT_MAX,LLONG_MAX);
}
9223372036854775808
2147483647,9223372036854775807
这是C17中~
的语义(加粗体),6.5.3.3.4。
〜运算符的结果是其(提升的)操作数的按位补码(即, 当且仅当未设置转换后的操作数中的相应位时,才设置结果。 整数 在操作数上进行提升,并且结果具有提升的类型。 type是一个无符号类型,表达式〜E等于其中可表示的最大值 该类型减去E。
这是C17 6.5.10.3中一元&
的语义。
通常对操作数进行算术转换。
a
等于9223372036854775808
,等于8000 0000 0000 0000(16)
。
没有后缀的整数常量1
与(int)1
相同。因此~1
== ~(int)1
== FFFF FFFE(16)
(~
不会进行整数提升)。
在{br />,FFFF FFFE(16)
的类型被转换为unsigned long long
a = 8000 0000 0000 0000(16) & FFFF FFFE(16)
通过通常的算术转换。因此a = 8000 0000 0000 0000(16) & FFFF FFFE(16)
== a = 8000 0000 0000 0000(16) & 0000 0000 FFFF FFFE(16)
最后,a = a & ~1
== a = 8000 0000 0000 0000(16) & FFFF FFFE(16)
== a = 8000 0000 0000 0000(16) & 0000 0000 FFFF FFFE(16)
== a = 0000 0000 0000 0000(16)
但输出显示为a = a & ~1
== a = 8000 0000 0000 0000(16) & FFFF FFFE(16)
== a = 8000 0000 0000 0000(16) & FFFF FFFF FFFF FFFE(16)
== a = 8000 0000 0000 0000(16)
解决方法
~1
在二进制补码系统(所有现代系统-包括您的PC使用的系统)中都是-2
。
-2(以4字节长的整数表示)的二进制表示形式为0xfffffffe
。
将其提升为long long integer
时,会保留值-2
,但二进制表示形式会更改:0xffffffffffffffffe
。此值是二进制变量AND
和变量a
的总和-因此其值保持不变。
如果要防止此行为,则需要告诉编译器要使用什么大小的数据:
a = a & ~(unsigned)1;
它会像您期望的那样运行
,我假设正常的带符号整数类型(例如int
在以下答案中具有2的补码表示,否则~1
的数值将不是-2。
整数常量1
的类型为int
。表达式~1
的值为-2,类型为int
。因此,a = a & ~1;
等效于a = a & -2;
。
由于a
的类型为unsigned long long
,而~1
的类型为int
,因此通过通常的算术转换,表达式~1
(数值-2)通过对数值进行数学上的加({无限宽度)一个多于unsigned long long
的值而将其转换为类型ULLONG_MAX
的值。因此a = a & ~1;
等同于a = a & -2;
,等同于a = a & (ULLONG_MAX - 1);
由于a
的值为0x8000000000000000ull
(相当于9223372036854775808ull
),而ULLONG_MAX
的最低有效64位的值为0xffffffffffffffffull
,因此最低(ULLONG_MAX - 1)
的有效64位值为0xfffffffffffffffeull
。由于0x8000000000000000ull & 0xfffffffffffffffeull
等于0x8000000000000000ull
,并且a
的前64位之外的所有位都为零,因此a
的值在分配中将保持不变。
我花了一些时间才真正弄清问题所在。
因此,我来不及击败P__J来获得真正具有启发性的技术答案。
因此,我将答案更改为另一个答案的可视化。
#include <stdio.h>
#include <limits.h>
int main()
{
unsigned long long a = 9223372036854775808ull; // a = 2^63
printf("%llx\n",a);
printf("%d\n",~1);
printf("%x\n",(unsigned) ~1);
printf("%llx\n",(unsigned long long )~1);
a = a & ~1;
printf("%llx\n",a);
printf("%x,%llx,%llx",INT_MAX,LLONG_MAX,ULLONG_MAX);
}
哪个输出为
8000000000000000
-2
fffffffe
fffffffffffffffe
8000000000000000
7fffffff,7fffffffffffffff,ffffffffffffffff
我认为它没有任何不可行之处。
我使用了评论中的一些建议,尤其是Jabberwocky的建议。
,“因此〜1 ==〜(int)1 == FFFF FFFE(16)”是错误的开始。
~(int)1
可能具有位模式 FFFF FFFE(16),但其值不变:仍然为-2(10)或-2( 16)。它的值没有4,294,967,294(10)或FFFF,FFFE(16)。
将-2(16)转换为unsigned long long
时,添加ULLONG_MAX + 1
* 变为18,446,744,073,709,551,614(10)或FFFF,FFFF,FFFF,FFFE(16)
8000 0000 0000 0000(16)
& FFFF FFFF FFFF FFFE(16)
= 8000 0000 0000 0000(16)
* 当unsigned long long
是64位时。
-
实际上,由于〜操作数已经为
int
,因此不会对它进行整数提升。 -
a & ~1;
等效于a & -2
(给定2的补码)。 -
操作数
a
的类型为unsigned long long
,操作数-2
的类型为int
。 -
根据通常的算术转换(C17 6.3.1.8)进行促销:
否则,如果具有无符号整数类型的操作数的秩更大或 等于另一个操作数类型的等级,然后是 有符号整数类型将转换为无符号操作数的类型 整数类型。
-
这意味着适用于有符号到无符号转换的规则(C17 6.3.1.3):
否则,如果新类型是无符号的,则通过重复加或 比新类型可以表示的最大值多减去一个 直到该值在新类型的范围内。
-
以上是指纯数学计算,无需关心类型。最大值
ULLONG_MAX
为2 ^ 64-1。根据上述规则,添加“更多”:-2 + 2 ^ 64-1 + 1 = 18446744073709551614 = 0xFFFFFFFFFFFFFFFE
-
现在,它与2的补码上的符号扩展到
-2
时获得的signed long long
值非常相同,但是实际上没有发生这种转换为更大的符号类型任何地方。 -
0x8000000000000000 & 0xFFFFFFFFFFFFFFFE
给出0x8000000000000000
。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。