C语言中负数的表示方法是什么?

26

C语言如何表示负整数?

是使用二进制补码表示还是使用最高位(MSB)来表示?

十六进制中的-1ffffffff

请给我解析一下。


1
当然是机器相关的,不过参考列表中有三种变体。字节顺序对于数字的内存布局非常重要:对于4个字节的ABCD、DCBA、BADC,可以使用(uint8_t*)intptr - Joop Eggen
4个回答

48

ISO C(在本例中为C99第6.2.6.2/2节,但它延续到标准的后续版本(a))规定实现必须选择三种不同的整数数据类型表示方式之一:二进制补码、反码或原码(尽管二进制补码实现远远超过其他实现的可能性极大)。

在所有这些表示中,正数是相同的,唯一的区别在于负数。

要获得正数的负表示形式,您需要执行以下操作:

  • 对于二进制补码,反转所有位然后加一。
  • 对于反码,反转所有位。
  • 对于原码,只需反转符号位。

您可以在下表中看到:

number | two's complement    | ones' complement    | sign/magnitude
=======|=====================|=====================|====================
     5 | 0000 0000 0000 0101 | 0000 0000 0000 0101 | 0000 0000 0000 0101
    -5 | 1111 1111 1111 1011 | 1111 1111 1111 1010 | 1000 0000 0000 0101

请记住,ISO并未强制要求在表示中使用所有位。他们引入了一个符号位、值位和填充位的概念。我从未真正看到使用填充位的实现,但是从C99基本原理文档中可以得到以下解释: 假设一台机器使用一对16位short(每个都有自己的符号位)来组成32位int,当在这个32位int中使用较低的short的符号位时会被忽略。然后,作为32位有符号int,有一个填充位(在32位的中间)被忽略以确定32位有符号int的值。但是,如果将此32位项视为32位无符号int,则该填充位对用户程序可见。 C委员会被告知有一台机器以这种方式工作,这就是添加填充位到C99的原因之一。
我相信他们所指的机器可能是Datacraft 6024(及其后继机型,均由Harris Corp生产)。在这些机器中,你有一个用于有符号整数的24位字,但是,如果你想要更宽的类型,你可以将两个字串在一起作为47位值,其中一个字的符号位被忽略。
+---------+-----------+--------+-----------+
| sign(1) | value(23) | pad(1) | value(23) |
+---------+-----------+--------+-----------+
\____________________/ \___________________/
      upper word            lower word

(a) 有趣的是,鉴于现代实现很少使用其他两种方法,人们一直在推动接受二进制补码作为唯一的方法。这在C++标准中已经取得了相当大的进展(WG21是负责此项工作的工作组),现在似乎也正在考虑应用于C语言中(由WG14负责)。


1
让我想知道为什么(表面上)最复杂的变体——二进制补码是最受欢迎的。翻转单个位(符号/大小)或翻转所有位(一补数)似乎更简单。我猜二进制补码的流行与其工作原理无关,而是由实现它的机器有多受欢迎(出于其他技术或营销原因)? - Frerich Raabe
2
@FrerichRaabe 直接从维基百科来的:“二进制算术不起作用。”本质上,加法器如何知道它是负数还是正数。http://simple.wikipedia.org/wiki/Signed_number_representations - JR Smith
@JRSmith 那个页面有点薄弱;过去确实存在过1的补码和符号-幅值。事实上,IEC60559浮点数使用符号幅值表示法,算术运算也完全正常。 - M.M
@FrerichRaabe:如果你认识到对于任何整数x和y以及整数N,当使用合理的编译器时,x+y、x-y或x*y的底部N位将不依赖于除x和y的底部N位之外的任何东西,那么二进制补码就是简单而美丽的。 - supercat
从C99的解释中复制粘贴错误的方式有20种。我觉得填充位可以在值位的中间是很有趣的。引用的文本有点自相矛盾,填充位对于有符号数来说是不可见的,但对于无符号数来说是可见的。这到底是什么意思呢?就C标准而言,它仍然是一个填充位,并不对值产生影响。你有什么想法? - 2501
显示剩余3条评论

12

C语言可以使用有符号整数的补码、反码和原码表示方法。大多数典型的硬件使用二进制补码表示整数,使用符号/大小表示法表示浮点数(还有一种可能是使用“偏置”表示浮点数指数)。


8
在十六进制中,-1表示为ffffffff。因此请明确这一点。
在二进制补码(目前最常用的表示法)中,除了最高有效位(MSB)之外,每个位从右到左(按大小顺序递增)都有一个值2的n次方,其中n从零开始逐一增加。 MSB的值为-2的n次方。
例如,在8位二进制补码整数中,MSB具有位置值-2的7次方(-128),因此二进制数1111 1111 2等于-128 + 0111 1111 2 = -128 + 127 = -1。
二进制补码的一个有用特性是,处理器的ALU只需要一个加法块即可执行减法,方法是形成右操作数的二进制补码。例如,10-6等价于10 +(-6); 在8位二进制中(为了简单起见)看起来像:
   0000 1010
  +1111 1010
   ---------
[1]0000 0100  = 4 (decimal)

[1]代表了被舍弃的进位位。再来看一个例子:10 - 11 == 10 + (-11)。

   0000 1010
  +1111 0101
   ---------
   1111 1111  = -1 (decimal)

两个补码的另一个特点是它只有一个表示零的值,而原码和反码各有两个:+0和-0。

1

对于整数类型,通常是二进制补码(实现特定)。对于浮点数,有一个符号位。


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