C - 当它始终编码为二进制的 2 的补码时,负字符中的带符号位在哪里——(-128)?
C - where is the signed bit in the negative char—(-128)—when it's always encoded as the 2's complement of its binary?
- 我了解带符号整数中的最高有效位用于对符号进行编码。
- 我还了解到负整数几乎总是被编码为其二进制的 2 的补码。
我无法调和上述两个事实。
如果我们在这里查看 -128 的示例,则在对其进行编码时会执行以下步骤
- 找出 128 的二进制等价物:
10000000
- 取 1 的补码:
01111111
- 1 与 1 的补码相加得到 2 的补码:
10000000
我的问题是:负整数的 sign bit
在哪里?换句话说,我想了解 10000000
如何解码为 -
128 而不是 -
0
- 如果最左边的 1 (MSB) 用于编码 -128 的负号,那不就留下了 7 位二进制 0000000,其十进制等效值为 0(而不是 -128)吗?
- 或者计算机(当然是比喻性的)——当对存储在某个内存位置的负整数执行计算时——以相反的顺序执行上面的步骤 1-3 以解码 1 字节 char 的值每当它看到 1 作为最高有效位(而不是 0)时?
- 或者MSB是在-128中对第8位(2^7)的符号和位进行编码。
我已经看到了 this 问题,但我的不同,因为我非常清楚 +128 不能存储在 1 字节中,因为它的签名二进制文件会转换为 0100000000
这需要9 位。
确切的布局取决于在机器上 (CPU),但在 C 上不。编译器是为一台特定的机器,并知道如何指示它正确处理它。
Where is the sign bit for a negative integer?
Door #3: "或者MSB是在-128
中同时编码第8位(2^7)的符号和位
使用 8 位有符号 char
,编码为 2 的补码 ,M 为 7 并且...
the sign bit has a value of -(2M) (C17dr § 6.2.6.2 2)
10000000
是 -128 + 0 * 64 + 0 * 32 + 0 * 16 + 0 * 8 + 0 * 4 + 0 * 2 + 0 * 1 --> -128
虽然二进制补码是迄今为止表示有符号整数值的最常见方式,但它并不是唯一的方式,并且 C 不需要二进制补码表示。
请注意,您不能在 any 表示中仅用 8 位表示 signed 128
- 您可以表示范围 [-127..127]
的补码或符号大小,或范围 [-128...127]
的补码。所以 根据定义 你需要超过 8 位来表示带符号的 128
:
two’s ones’ sign-magnitude
125 01111101 01111101 01111101
126 01111110 01111110 01111110
127 01111111 01111111 01111111
-128 10000000 n/a n/a
-127 10000001 10000000 11111111
-126 10000010 10000001 11111110
-125 10000011 10000010 11111101
对于任何使用整数值的数据类型,如“int”、“short”、“byte”、“char”、“long”,除了“unsigned”,它们的值范围为:( -1 * 2(总位数 - 1)) 到 (2(总位数 - 1)) - 1。最左边的位用作符号位。
对于 char 数据类型,其大小为 1 个字节。的位是 8 位。 1 最左边的位用于符号,其余位用于值。所以我们得到的没有符号的值来自
0 到 127 即 (00000000)2 到 (01111111)2
当它与符号位一起时,它的范围是
-128 到 -1 即 (10000000)2 到 (11111111)2.
If we look at the example of -128 here, the following steps are taken while storing it…
存储时不是,calculating/converting时是。当编译器在源代码中处理 -128
时,或者当我们在纸上看到它并使用它时,我们会做任何我们想做的计算。我们可以使用位、数字或纸上的标记来实现我们想要的任何东西。当我们产生最终答案时,然后 最终答案中的位具有它们的最终含义。中间步骤不必以相同的方式使用位。
给定“128”,我们计算出这是纯二进制的 10000000(无符号)。那么我们可以计算出它的补码表示,将01111111的位补1(还是纯二进制,无符号),得到10000000。那么这些相同的位就是补码表示。
字节被解释时,包括用于算术运算或将二进制补码表示形式转换为十进制时,高位将被解释为符号位。但是,同样,我们不需要在整个计算过程中以相同的方式使用位。我们可以取 10000000,看到高位被设置为告诉我们这个数是负数,然后像以前一样取它的二进制补码:将这些位补码为 01111111 然后加一得到 10000000。现在我们有相同的位,但是它们是纯二进制数,没有符号。它们代表128,我们知道它是负数,因为我们之前观察到了原始符号位。
另请注意,signed char x
和 unsigned char y
使用相同的位模式来表示不同的值。当x
的位模式为11111111时,表示-1。当 y
的位模式为 11111111 时,它表示 255。为了使其工作,编译器将对 x
的操作与 y
的操作使用不同的指令。使用有符号类型与使用无符号类型有不同的说明。 (其中许多在很大程度上重叠;加法和减法通常使用相同的指令执行,但标志结果的解释不同,以检测溢出和其他情况。)
此外,对于这个 single-byte 示例,编译器通常不会将其作为 char
使用。在源文本中,128
是一个 int
常量。在内部,编译器可能会将 128
转换为 32 位 int
,然后将其取反以生成 −128,位为 11111111111111111111111110000000,然后将其存储在带符号的 char
中,它取低八位,10000000。(这可能因编译器而异。)
有趣的是,这个边界问题确实影响了 -2147483648
的类型。考虑使用 32 位 int
和 64 位 long
的 C 实现。 −2,147,483,648 可表示为 32 位 int
,但在 C 语法中,-2147483648
不是常量,而是 -
和 2147483648
的组合。而且,由于 2,147,483,648 无法用 32 位表示,因此它是 long
常量。所以-2147483648
的类型是long
。您可以通过以下方式验证:
printf("%zu %zu\n", sizeof -2147483647, sizeof -2147483648);
它将在 C 实现中用 32 位打印“4 8”int
。
(这引发了如何定义 INT_MIN
的问题。它的值必须为 −2,147,483,648,但类型必须为 int
。)
- 我了解带符号整数中的最高有效位用于对符号进行编码。
- 我还了解到负整数几乎总是被编码为其二进制的 2 的补码。
我无法调和上述两个事实。
如果我们在这里查看 -128 的示例,则在对其进行编码时会执行以下步骤
- 找出 128 的二进制等价物:
10000000
- 取 1 的补码:
01111111
- 1 与 1 的补码相加得到 2 的补码:
10000000
我的问题是:负整数的 sign bit
在哪里?换句话说,我想了解 10000000
如何解码为 -
128 而不是 -
0
- 如果最左边的 1 (MSB) 用于编码 -128 的负号,那不就留下了 7 位二进制 0000000,其十进制等效值为 0(而不是 -128)吗?
- 或者计算机(当然是比喻性的)——当对存储在某个内存位置的负整数执行计算时——以相反的顺序执行上面的步骤 1-3 以解码 1 字节 char 的值每当它看到 1 作为最高有效位(而不是 0)时?
- 或者MSB是在-128中对第8位(2^7)的符号和位进行编码。
我已经看到了 this 问题,但我的不同,因为我非常清楚 +128 不能存储在 1 字节中,因为它的签名二进制文件会转换为 0100000000
这需要9 位。
确切的布局取决于在机器上 (CPU),但在 C 上不。编译器是为一台特定的机器,并知道如何指示它正确处理它。
Where is the sign bit for a negative integer?
Door #3: "或者MSB是在-128
中同时编码第8位(2^7)的符号和位使用 8 位有符号 char
,编码为 2 的补码 ,M 为 7 并且...
the sign bit has a value of -(2M) (C17dr § 6.2.6.2 2)
10000000
是 -128 + 0 * 64 + 0 * 32 + 0 * 16 + 0 * 8 + 0 * 4 + 0 * 2 + 0 * 1 --> -128
虽然二进制补码是迄今为止表示有符号整数值的最常见方式,但它并不是唯一的方式,并且 C 不需要二进制补码表示。
请注意,您不能在 any 表示中仅用 8 位表示 signed 128
- 您可以表示范围 [-127..127]
的补码或符号大小,或范围 [-128...127]
的补码。所以 根据定义 你需要超过 8 位来表示带符号的 128
:
two’s ones’ sign-magnitude
125 01111101 01111101 01111101
126 01111110 01111110 01111110
127 01111111 01111111 01111111
-128 10000000 n/a n/a
-127 10000001 10000000 11111111
-126 10000010 10000001 11111110
-125 10000011 10000010 11111101
对于任何使用整数值的数据类型,如“int”、“short”、“byte”、“char”、“long”,除了“unsigned”,它们的值范围为:( -1 * 2(总位数 - 1)) 到 (2(总位数 - 1)) - 1。最左边的位用作符号位。
对于 char 数据类型,其大小为 1 个字节。的位是 8 位。 1 最左边的位用于符号,其余位用于值。所以我们得到的没有符号的值来自
0 到 127 即 (00000000)2 到 (01111111)2
当它与符号位一起时,它的范围是
-128 到 -1 即 (10000000)2 到 (11111111)2.
If we look at the example of -128 here, the following steps are taken while storing it…
存储时不是,calculating/converting时是。当编译器在源代码中处理 -128
时,或者当我们在纸上看到它并使用它时,我们会做任何我们想做的计算。我们可以使用位、数字或纸上的标记来实现我们想要的任何东西。当我们产生最终答案时,然后 最终答案中的位具有它们的最终含义。中间步骤不必以相同的方式使用位。
给定“128”,我们计算出这是纯二进制的 10000000(无符号)。那么我们可以计算出它的补码表示,将01111111的位补1(还是纯二进制,无符号),得到10000000。那么这些相同的位就是补码表示。
字节被解释时,包括用于算术运算或将二进制补码表示形式转换为十进制时,高位将被解释为符号位。但是,同样,我们不需要在整个计算过程中以相同的方式使用位。我们可以取 10000000,看到高位被设置为告诉我们这个数是负数,然后像以前一样取它的二进制补码:将这些位补码为 01111111 然后加一得到 10000000。现在我们有相同的位,但是它们是纯二进制数,没有符号。它们代表128,我们知道它是负数,因为我们之前观察到了原始符号位。
另请注意,signed char x
和 unsigned char y
使用相同的位模式来表示不同的值。当x
的位模式为11111111时,表示-1。当 y
的位模式为 11111111 时,它表示 255。为了使其工作,编译器将对 x
的操作与 y
的操作使用不同的指令。使用有符号类型与使用无符号类型有不同的说明。 (其中许多在很大程度上重叠;加法和减法通常使用相同的指令执行,但标志结果的解释不同,以检测溢出和其他情况。)
此外,对于这个 single-byte 示例,编译器通常不会将其作为 char
使用。在源文本中,128
是一个 int
常量。在内部,编译器可能会将 128
转换为 32 位 int
,然后将其取反以生成 −128,位为 11111111111111111111111110000000,然后将其存储在带符号的 char
中,它取低八位,10000000。(这可能因编译器而异。)
有趣的是,这个边界问题确实影响了 -2147483648
的类型。考虑使用 32 位 int
和 64 位 long
的 C 实现。 −2,147,483,648 可表示为 32 位 int
,但在 C 语法中,-2147483648
不是常量,而是 -
和 2147483648
的组合。而且,由于 2,147,483,648 无法用 32 位表示,因此它是 long
常量。所以-2147483648
的类型是long
。您可以通过以下方式验证:
printf("%zu %zu\n", sizeof -2147483647, sizeof -2147483648);
它将在 C 实现中用 32 位打印“4 8”int
。
(这引发了如何定义 INT_MIN
的问题。它的值必须为 −2,147,483,648,但类型必须为 int
。)