在C语言中如何得到一个整数的二进制补码?

3

如何在C语言中获得int的二进制补码?

例如,如果我有一个整数,比如-254,我该如何将其转换为100000010?

是否有办法从整数变量中提取二进制补码值,因为在C中,ints存储在二进制补码中?


4
你需要编写自己的函数。这里是一个起点 https://dev59.com/8kfRa4cB1Zd3GeqP5w6- - michael.schuett
4
我知道在C语言中,整数以二进制补码形式存储——但这并不是绝对的!其他表示法也是允许的,比如反码和原码。只是二进制补码是现代硬件中使用最普遍的格式。 - user2357112
请参考以下链接:http://stackoverflow.com/questions/21837008/how-to-convert-from-sign-magnitude-to-twos-complement - Gehan Fernando
我需要它适用于现代硬件场景。 - Thahleel al-Aleem
2
很遗憾,这个问题被错误地标记为重复,因为链接的问题绝对不是重复的。此外,这里的答案要么令人困惑,要么误导人,事实上,答案就是将有符号整数转换为无符号整数 - C保证得到的无符号值具有相同的位模式作为二进制补码表示,即使在使用其他表示形式的整数的机器上,因为无符号转换是通过添加UINT_MAX + 1直到结果为正来定义的,这会导致有符号值的二进制补码模式。 - Remember Monica
0x1ffU & -254 可以翻译为:0x1ffU和-254进行按位与操作。 - Remember Monica
4个回答

8
如果你在处理无符号整数unsigned int,那么你可以反转位~并加上1来得到2的补码值。x=(~y)+1;如果你的机器使用2的补码表示signed int,那么根据实现的定义,这应该转换为正确的signed int值。
C语言本身在这个领域的保证有点模糊。要在对象的位表示上可移植地工作,你应该使用一个unsigned类型。

正如我的示例所建议的那样,整数是有符号的,因此会出现复杂情况。 - Thahleel al-Aleem
在表达式内部进行变量转换,并在退出时进行转换。如果它不是2s补码机器,那么它就不是正确的“int”,但它仍然是“2s补码”...[在实现定义的转换后由机器解释]。 - luser droog
为什么要使用类似~(unsigned)y)+1的身份标识,而不是直接使用0U - y或一元运算符-(unsigned)y?二进制补码使用与无符号加/减(和非扩展乘法)相同的二进制操作,并且C已经有一个取反运算符。 - Peter Cordes
1
@PeterCordes 只有在你需要最大的可移植性的非常罕见的情况下,才会这样做,因为 C 本身并不保证 2s 补码。或者,仅仅是为了说明位操作的过程,以供教育目的。 - luser droog
@luserdroog:我的两个表达式都没有涉及有符号整数类型的 C 操作。二进制补码的减法 - 和二进制减法是一样的,所以我们可以用无符号减法在 C 中实现它。你的版本依赖于无符号二进制加法,而我的版本则依赖于无符号二进制减法。不需要单独的加、减、取反指令(只需要除法和扩展乘法)是计算机使用二进制补码表示有符号数的原因之一。 - Peter Cordes
1
我猜提到 1 + ~(unsigned)y 相当于使用二进制补码恒等式是有意义的,但从其他SO问题来看,很多初学者会陷入其中,并没有意识到它与从0中减去相同。例如,我看到[汇编]问题,人们实际上使用了 not / inc 指令,而不是 neg(在x86上是二进制补码机器),因为他们没有意识到二进制补码求反是二进制求反。至少在C语言中,您可以让编译器在针对二进制补码机器进行编译时将其转换回有符号的 - - Peter Cordes

7

知道在C语言中,整数使用二进制补码存储。

虽然不能保证所有计算机都是如此,但在实践中几乎所有计算机都使用二进制补码。

有没有办法从整数变量中提取出二进制补码值?

它已经是用二进制补码格式存储的,所以不清楚你在问什么。似乎你是想知道如何以二进制格式打印变量?

int data = -254;
const size_t BITS = 8*sizeof(data);
char bin_str[BITS+1];

for(unsigned int i=0; i<BITS; i++)
{
  unsigned int mask = 1u << (BITS - 1 - i);
  bin_str[i] = (data & mask) ? '1' : '0';
}
bin_str[BITS] = '\0';

-1

如果一个数是负数,你可以通过从0x80000000中减去该数来将其转换为二进制补码。这适用于使用二进制补码表示负值的32位整数机器,但如果该值为正,则会导致二进制补码取反。对于二进制补码负数的右移将会在左侧填充1,我们可以利用这一点来创建一个掩码,以选择原始值或将有符号幅值负值转换为二进制补码负值。

int sm2tc(int x) {
  int m = x >> 31;
  return (~m & x) | (((x & 0x80000000) - x) & m);
}

原始代码由Apriori发布


2
0x80000000是一个无符号字面量,所以这段代码有错误。首先,不要在有符号类型上使用位运算符。 - Lundin

-3

实际上,有一种简单的方法可以做到这一点。只需将您的数字转换为二进制字符串,然后将该字符串转换回整数即可。

使用 itoa,它可以将一个数字以字符串形式转换为给定的基数。

http://www.cplusplus.com/reference/cstdlib/itoa/

然后,只需使用熟悉的atoi将其转换回int。


这不是一个好主意。 - MooseBoys
itoa 不是标准函数,也不适用于 gcc - riteshtch
...就像我发布的链接中所说的那样,如果您需要可移植性,可以使用sprintf()。 - hamzamuhammad

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接