【问题标题】:Why does -INT_MIN = INT_MIN in a signed, two's complement representation?为什么 -INT_MIN = INT_MIN 在有符号的二进制补码表示中?
【发布时间】:2012-02-13 14:11:36
【问题描述】:

我仍然没有找到为什么最小的有符号负数没有等效的有符号正数的原因? 为简单起见,我的意思是 3 位二进制数 100是-4?但我们不能有一个正数的签名格式,因为我们不能。它溢出了。 那么我们如何知道二进制补码 1000 是 -4 1000 0000 是 -128 等等?我们没有原始的正数

【问题讨论】:

  • 因为要数0。[-4,-1]包含4个数字,[0,3]包含4个数字,一共8个,3位二进制数有2个3 (=8) 种可能组合的幂。
  • 什么?抱歉,在 2^3 = 8 种可能的组合之前,我不明白您写的任何内容。
  • 您有 4 个负数、3 个正数和 1 个零。共 8 个!
  • [-4,-1] 是数字 -4、-3、-2 和 -1 的集合。 [0,+3] 是数字 0、1、2 和 3 的集合。总区间的每一半都有 4 个数字,这就是分配给 3 位表示的所有 8 个插槽。
  • 为了记录,这也在聊天中讨论过:chat.stackoverflow.com/transcript/message/2401301#2401301

标签: c++ c binary signed twos-complement


【解决方案1】:

一种思考方式是,有符号二进制补码格式的工作原理是为每个位分配一个 2 的幂,然后翻转最后一个 2 的幂的符号。我们以-4为例,它表示为100。这意味着该值为

-1 x 2^2 + 0 x 2^1 + 0 x 2^0

如果我们想得到这个值的正数,我们必须取反才能得到

 1 x 2^2 - 0 x 2^1 - 0 x 2^0

注意这个值等于

 1 x 2^2 + 0 x 2^1 + 0 x 2^0

换句话说,这个值的正常二进制表示是 100。但是,我们在这里遇到了麻烦,因为我们使用的是带符号的二进制补码表示,这意味着我们专门保留了 4 位作为符号少量。因此,当我们尝试将位模式 100 解释为带符号的三位二进制补码值时,它会返回与我们开始时相同的结果。位的短缺是这里的痛点。

更一般地说,给定 n 位,其中第一个是二进制补码表示中的符号位,尝试计算 -1000...00 将返回相同的值,因为存储大正值所需的位具有赋予它的特殊含义。

那么为什么要这样做呢?这样做的原因是,如果只有 n 位,则无法存储值 -2n - 1 到 2n - 1,因为有 2 n + 1 个不同的数字,只有 2^n 个不同的位模式。因此,排除最大的正数可以在指定的位模式中保存所有不同的数字。

但是为什么要放弃高价值而不是低价值呢?这是为了保持与无符号整数的二进制兼容性。在无符号整数中,值 0 到 2n-1 - 1 都使用标准的以二为底的表示进行编码。因此,为了使无符号整数和有符号整数完全一致,无符号整数被设计为与前 2n - 1 个无符号整数逐位等效,范围从 0 到 2 n - 1 - 1,包括在内。在此之后,无符号值需要最高有效位来编码数字,但有符号值使用此作为符号位。

希望这会有所帮助!

【讨论】:

  • 啊,我明白了。谢谢你这么好的解释。
【解决方案2】:

-INT_MIN 是整数溢出,在 C 中是未定义的行为。

-INT_MIN 保证等于INT_MIN 只有当有符号整数溢出时才回绕。这可以通过gcc 启用,例如使用-fwrapv 选项。

编译器通常利用整数溢出是 C 中未定义的行为这一事实来执行一些优化。依赖于有符号整数溢出是不安全的。

一个众所周知的编译器优化示例如下

#define ABS(x) ((x) > 0 ? (x) : -(x)) 

void foo(int x){  
    if (ABS(x) >= 0) {
        // some code
    }
}

如今大多数启用了优化选项的编译器(gccicc)都会优化测试,因为-INT_MIN 是未定义的行为。

【讨论】:

  • 准确地说,-fwrapv 是确保有符号整数模环绕的正确标志。 -fno-strict-overflow 阻止优化器假设整数溢出永远不会发生。
  • @nucleon -fwrapv 是这里的正确选项,谢谢我编辑了我的答案。
【解决方案3】:

A. n位二进制数有偶数种可能,所以我们不能表示正负数相同的范围。

B.我们希望每个以 1 开头的数字都是负数,每个以 0 开头的数字都是非负数。 (不是相反,因为我们希望在有符号和无符号中将相同的表示为正数和零。因此,0 在正数的一半中,因此它们少了一个位置。

【讨论】:

    【解决方案4】:

    two's complement 的替代品具有这样的属性,称为one's complement
    在一个人的补码形式中,可能的最低值也具有有效的正数形式。


    一个补码的工作原理是简单地将数字本身的所有位取反。
    例如,我们知道0110 == 6 和一个补码1001 == -6。使用反码,我们得到的正数和负数一样多。

    但是位表示1111 呢?光看就知道它是零的“负”形式(0000 = 0; 1111 = -0),但是这样的数字没有任何意义,有点浪费。

    相反,我们使用二进制补码,它类似于一个补码,但是在反转位之后,我们加一。所以如果我们知道0110 = 6,那么一个补码是1001,而二进制补码是1001 + 1 == 1010。使用二进制补码,我们没有“负零”,因为它会导致溢出。

    另一种看待它的方式是“如果设置了最高位,则该数字为负数”。这意味着正范围是[0 .. 2^(bits - 1)],负范围是其他所有内容。正数的数量与负数的数量相同,但是因为(在这种格式中)零被认为是正数,所以负数范围被移到 [-1 .. (neg) 2^(bits - 1)]


    假设我们正在处理二进制补码中的 3 位有符号数。这会给我们下表:

    BITS  VALUE
    000       0
    001       1
    010       2
    011       3
    
    100      -4
    101      -3
    110      -2
    111      -1
    

    你可以看到正数和负数的数量是一样的,只是负数不像正数那样从0开始。

    【讨论】:

    • 二进制补码整数将最左边的位视为产生一个值,该值应复制到其左侧的所有位[对于任何 N,从最右边 N 位为 0 的值中减去 1,最右边结果的 N 位将是 1],因此 -1 相当于无限的 1 序列。对于一个的补码,使用相同的值填充左侧和右侧。值 0.1111... 和 ...111.0 [在任何一种情况下都有无穷多个 1] 分别等于 1.0 和 ...110.111... [在后一种情况下两边都有无穷多个],但只有后两个表单被规范化。
    【解决方案5】:

    缺少的数字是0。在数学意义上,0 既不是正数也不是负数。但在二进制意义上,因为0 没有负位,所以它被认为是正的。换句话说,如果你想要 -128 到 128,就不可能有 0。

    【讨论】:

    • 但我们有 0,即 000 -3 位表示。 -4 来自哪里?由于我们不能将 4 表示为已签名
    • 跟我一起数数:3 位 = 2^3 选择 = 8 种可能的组合。它们是:-4、-3、-2、-1、0、1、2、3。根据我的回答,这里有 4 个正数和 4 个负数。在这种情况下,-4 将是 111,而 3 将是 011
    • 是的,我明白了。但是 128 如何确保我们没有 0?我知道我们不能有 128,因为它是 -128,因为 MSB 充当负号。
    • @LewsTherin:你不能在二进制补码表示中做到这一点。你可以想出另一个没有 0 的表示。
    • @LewsTherin 我的发言有点草率。更准确的说法是,如果有 +128,您需要在其他地方删除一个数字。我选择 0 是因为它看起来很荒谬。更现实地说,你可能会下降 -128。
    【解决方案6】:

    因为你必须数 0。整数范围 [-4,-1](或等效地 -4,-3,-2 和 -1)包含 4 个数字,其余的范围 [0,3] (或等价于 0、1、2 和 3)包含 4 个数字,总共 8 个,3 位二进制数具有 2 的 3 次方(=8)个可能的组合。

    这样想。 [-n,+n] 形式的任何整数范围都必须具有奇数大小(2*n+1 个整数)。无论您使用何种整数二进制表示,其负数和正数的数量都会不同,因为组合的数量始终是偶数(2 的幂)。

    【讨论】:

      【解决方案7】:

      那么我们怎么知道补码 1000 是 -4 1000 0000 是 -128 等等?我们没有原始的正数

      您的错误是认为我们需要正数的二进制补码表示来计算负数的二进制补码表示。

      求负数的补码的过程是:

      从要表示的数字的绝对值的正常非二进制补码表示开始。所以对于-4,取|-4|的非二进制补码表示,100。

      翻转所有位:100 -> 011(或 ...11111011 向左无限延伸)。

      加一个:011 -> 100(或...11111100)

      现在截断为您正在使用的位数(这消除了进位位或无限的 1 字符串)。因此,100 是 -4 的 3 位二进制补码表示。

      反之,取二进制补码表示 (100) 翻转位 (011) 并添加一个 (100),您现在就有了 |-4| 的非二进制补码表示。 1 * 2 ^ 2 + 0 * 2 ^ 1 + 0 * 2 ^ 0 = 4。因此我们知道我们开始的表示,100,是-4的3位二进制补码表示。

      猜你喜欢
      • 1970-01-01
      • 2014-11-18
      • 2013-05-28
      • 1970-01-01
      • 2020-03-12
      • 2011-10-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多