微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

按位和超过32位

如何解决按位和超过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;

它会像您期望的那样运行

https://godbolt.org/z/G6757W

,

我假设正常的带符号整数类型(例如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 举报,一经查实,本站将立刻删除。