【问题标题】:How to force compiler or interpreter to see binary numbers two's complement?如何强制编译器或解释器查看二进制数的二进制补码?
【发布时间】:2015-05-05 07:39:11
【问题描述】:

这是一个简单的C++程序:

#include <iostream>
#include <string>
using namespace std;

int main()
{
int x=0b10000001;
cout<<"Your number is :"<<x;
}

输出:

您的号码是:129

这也是它的 Python 对应物:

>>> i=0b10000001
>>> print i
129
>>> 

我想知道,如何强制 C 编译器或 Python 解释器将我的变量视为二进制补码并打印 -127 而不是 129

我可以编写一个函数来做我想做的事(即检查最高有效位并打印所需的值),但我想知道,数字在哪里表现为二进制补码,以及它们在编程中何时表现为普通二进制值?

问题是:

二进制变量何时表现为二进制补码,何时表现为普通二进制数?

【问题讨论】:

  • 使用signed char 代替int? (假设您的设备以这种方式工作)
  • 呃,在 C/C++ 中,您必须跳过几圈。将其设为signed char,然后将其转换为int 即可。顺便说一句,你的二进制补码不是-1,而是-127。
  • @deviantfan 很好。我将x 定义为char / signed char 并在下一行中,在打印之前将其转换为int。现在我看到-127。但问题不是“如何?”,而是“为什么?”
  • @Abraham 如果变量长度为 8 位,则会发生 127+1==-128 处的溢出,但 int 大于此值。它可以在溢出之前保存更大的数字。 (严格来说,尺寸取决于您的设备)
  • @Aracthor 不完全是,整数字面量是,除非另有说明(使用例如后缀)类型为int,默认情况下是有符号的。

标签: python c++


【解决方案1】:

我知道的最简单的不强制转换方式:

i = i - 2*(i & (1 << 7))

其中i 是任何整数类型,至少足以满足您的目标值,而7 应替换为符号位的位置(7 表示 8 位数字,15 表示 16 位数字,等等)。

之所以有效,是因为有符号整数中的符号位最好被解释为其通常值的。例如,8 位数字的符号位表示 -128,而不是 +128。这个小sn-p代码基本上减去符号位的两倍,将+128变成-128。

Python 示例:

>>> i = 129
>>> i = i - 2*(i & (1 << 7))
>>> i
-127

在编程时,值的类型决定了程序如何解释该值的位模式。无符号值解释没有任何符号位的位模式(因此值不能为负),而有符号值则使用符号位解释它(二进制补码)。在 C 中,符号位是固定宽度整数的最高有效位。通过强制将值强制转换为较小的大小,您可以更改将哪个位视为符号位;这就是为什么要打印

(signed char)129

在 C 中会给你 -127。

请注意,Python 没有内置的无符号类型;所有整数都有符号,但它们可以任意大(与 C 不同,其中整数被限制为适合特定数量的位)。因此,我的回答向您展示了如何在不使用强制转换的情况下获得有符号值;它适用于 C 和 Python。

【讨论】:

    【解决方案2】:

    常量 0b10000001 的八位设置为 1。

    当加载到 32-ou 64 位 int 变量 x 时,最左边的位为 0,因此它正值。

    【讨论】:

    • 没错。我尝试在x 中保存一个 32 位二进制数并打印它,它现在表现为二进制补码。
    【解决方案3】:

    负数的二进制表示并不像你想象的那样工作。如果设置了符号位,则它不是简单的二进制补码。所以对数字的符号翻转操作只是像x ^= (1 &lt;&lt; 7)那样设置符号位。相反,二进制数的二进制表示是x = ~x + 1。如果 x 然后被解释为有符号整数,它将打印负值,如果您将整数视为 unsigned,您将得到 255 - x + 1,无论它是什么。

    对于8bit的位宽,看这个例子:

    #include <iostream>
    
    int main()
    {
       int8_t x = 0b10000001;
       int8_t y = 0b11111111;
       std::cout << "x as signed int is :" << int(x) << '\n';  // prints -127
       std::cout << "x as unsigned is :  " << int(uint8_t(x)) << '\n'; // 129
       std::cout << "y as signed int is :" << int(y) << '\n';          // -1
       std::cout << "y as unsigned is :  " << int(uint8_t(y)) << '\n'; // 255
       uint8_t z = 1;
       std::cout << "z ^ (1 << 7) = " << int(int8_t(z ^ (1 << 7))) << '\n'; // -127
       std::cout << "~z + 1       = " << int(int8_t(~z + 1)) << '\n';       // -1
    }
    

    将值打印到标准输出时,您必须将其转换回int,否则它将被解释为char,这很可能是不可打印的字符。

    由于int8_t是C++11的扩展,所以你必须用--std=c++11编译这个程序。

    【讨论】:

    • int8_t 很棒,谢谢。但我不同意你回答的第一部分。我尝试打印int x=0b11111111111111111111111111111111;,它返回-1。所以我认为@Michel Billaud 的回答是正确的。你同意吗? 请注意,我的计算机中的整数长度为 4 个字节
    • 嗯,这取决于你的意思。如果您将 x 视为 int8_t,则它的第 8 位被设置并且它是一个负数:0b10000001。如果相同的位模式是 16 位数字,它看起来像 0b0000000010000001。当然,它的符号位为零,因此是一个正数。这就是为什么你必须非常小心你如何投射数字。反正我的回答和他的不矛盾。
    • 我将二进制数保存在 [signed]int 变量中。我以为他们将 int8_t 定义为 8 位整数,其中第 8 位作为符号位,而不是 int 变量中的第 32 位。
    • int8_t 不会简单地将符号位放在另一个地方。这意味着它是一个 short int。它只使用 8 位而不是 32 位(或任何最近的系统上的 64 位)。
    【解决方案4】:

    这是在 python 中执行此操作的一种有趣方式:

    >>> from struct import *
    >>> i = 0b10000001
    >>> print unpack('b', pack('B', i))[0]
    -127
    >>>
    

    pack() 基于转换代码返回数字的字节字符串表示形式。

    pack('B', i)'\x81' IE 一个单字符字节字符串,该字符是无符号二进制表示(由'B'指定)为10000001的那个字符

    unpack() 接受一个字节字符串,然后根据每个字节的转换代码将其转换为数字列表。

    unpack('b', '\x81') 返回 (-127),因为这是二进制 10000001 在解释为带符号二进制字符时的含义(这是“b”指定的内容)。

    [0] 用于选择unpack() 返回的列表的第一个(也是唯一一个)元素。

    【讨论】:

    • 请问您可以添加描述吗?
    • 当然 - 希望它有意义:)
    【解决方案5】:

    只使用一次值的表达式:

    >>> (0b10000001 + 128) % 256 - 128
    -127
    

    或者(在看到 nneonneo 的回答之后):

    >>> i - (i >> 7 << 8)
    -127
    

    【讨论】:

    • 这里涉及的机制是符号扩展。当将 int_8 中的值分配给更大的(有符号的)整数时,最左边的位(符号)被传输到 all 额外的左边位。因此,它保留了相同数字的预期含义,以小整数或大整数表示。试试int8_t x = 0b10000001; int y = x; cout &lt;&lt; x;,它返回 -127
    • 在 Python 中,(i if i&lt;128 else i-256) 对我来说似乎更简单。只需比较和减去。
    • @MichelBillaud 是的,当然 C 有其他选择,我只是用 Python。我承认我什至没有想到 if-else,我猜是因为我不想重复这个值。或者因为我只是习惯了我展示的方式。我喜欢你的,但就个人而言,我还是更喜欢我的。不过,可能取决于实际的代码上下文。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-04-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多