我不知道为什么微处理器系统实现无符号数字。我猜代价只是条件分支数的两倍,因为大于等值的.etc需要与带符号的算法不同的算法,还有无符号数对它有很大好处的算法吗?
我的问题部分是为什么它们需要位于指令集中而不是由编译器支持?
我不知道为什么微处理器系统实现无符号数字。我猜代价只是条件分支数的两倍,因为大于等值的.etc需要与带符号的算法不同的算法,还有无符号数对它有很大好处的算法吗?
我的问题部分是为什么它们需要位于指令集中而不是由编译器支持?
Answers:
无符号数字是对位序列的一种解释。这也是CPU内部最简单,最常用的解释,因为地址和操作码只是位。内存/堆栈寻址和算术是微处理器处理的基础。在抽象金字塔之上,对位的另一种常见解释是作为字符(ASCII,Unicode,EBCDIC)。然后还有其他解释,例如IEEE浮点,图形的RGBA等。这些都不是简单的带符号数字(IEEE FP并不简单,使用这些数字的算法非常复杂)。
同样,使用无符号算法,很容易(如果不是最有效的话)实现其他算法。反之则不正确。
比较操作的大部分硬件成本是减法。用于比较的减法输出本质上是三个状态位:
通过在减法运算之后测试这三个位的适当组合,我们可以确定所有带符号的关系运算以及所有无符号的关系运算(这些位也是如何检测溢出,有符号还是无符号)。可以共享相同的基本ALU硬件来实现所有这些比较(更不用说减法指令了),直到对这三个状态位进行最终检查为止,这根据所需的关系比较而有所不同。因此,它不是很多额外的硬件。
唯一的实际成本是需要对指令集体系结构中的其他比较模式进行编码,这可能会稍微降低指令密度。尽管如此,硬件具有许多给定语言未使用的指令还是很正常的。
因为,如果您需要计数始终为的值 >= 0
,则不必要使用带符号整数将计数空间减少一半。
考虑一下您可能要放在数据库表中的自动递增的INT PK。如果你使用一个有符号整数那里,你的表存储一半的记录,因为它可能为同一字段大小而没有益处。
或RGBa颜色的八位字节。我们不想笨拙地开始将这个自然为正数的概念计为负数。一个有符号的数字会破坏思维模式或使我们的空间减半。无符号整数不仅与概念匹配,而且提供两倍的分辨率。
从硬件的角度来看,无符号整数很简单。它们可能是执行数学运算的最简单的位结构。而且,毫无疑问,我们可以通过在编译器中模拟整数类型(甚至是浮点!)来简化硬件。那么,为什么在硬件中同时实现无符号和有符号整数?
好吧…… 表现!
在硬件中实现带符号整数比在软件中实现效率更高。可以指示硬件在一条指令中对任一整数类型执行数学运算。这非常好,因为硬件或多或少并行地将位粉碎在一起。如果您尝试在软件中进行模拟,那么选择“模拟”的整数类型无疑将需要许多指令,而且速度明显慢。
您的问题包括两部分:
无符号整数的用途是什么?
无符号整数值得麻烦吗?
简单来说,无符号数字代表一类负数没有意义的数量。当然,您可能会说“我有多少个苹果?”这个问题的答案。如果您欠某人一些苹果,则可能为负数,但是“我有多少内存”的问题呢?-您的内存容量不能为负。因此,无符号整数非常适合表示此类数量,并且它们的优点是能够表示正值范围的两倍。例如,您可以使用16位带符号整数表示的最大值是32767,而使用16位无符号整数可以表示的最大值65535。
无符号整数并不真正代表任何麻烦,因此,是的,它们是值得的。您会看到,它们不需要额外的“算法”集;实现它们所需的电路是实现有符号整数所需的电路的子集。
CPU没有带符号整数的乘数,而没有符号整数的乘数。它只有一个乘法器,根据运算的性质,其工作方式略有不同。支持有符号乘法比无符号乘法需要更多的电路,但是由于无论如何都需要支持,无符号乘法实际上是免费的,因此它包含在软件包中。
至于加减法,电路完全没有区别。如果您读过所谓的整数的二进制补码表示法,就会发现它的设计是如此巧妙,以至于无论整数的性质如何,这些运算都可以完全相同的方式执行。
比较也以相同的方式工作,因为它只不过是减除结果,所以唯一的区别是条件分支(跳转)指令,该指令通过查看由CPU设置的不同标志来工作。前面的(比较)指令。在以下答案中:https : //stackoverflow.com/a/9617990/773113您可以找到有关它们如何在Intel x86架构上工作的解释。发生的情况是将条件跳转指令指定为有符号还是无符号取决于它检查的标志。
微处理器本质上是无符号的。带符号的数字是实现的东西,而不是相反的东西。
计算机可以并且确实可以在没有带正负号的情况下正常工作,但是对我们来说,需要负数的人类因此发明了带正负号。
具有纯数学背景,对于任何有兴趣的人来说,这都是数学上的一点点。
如果我们以一个8位有符号和无符号整数开始,那么就加法和乘法而言,我们所拥有的基本上是模256的整数,前提是使用2的补码表示负整数(这就是每个现代处理器的方式) 。
区别在两个地方:一个是比较操作。在某种意义上,最好将整数256模作为一个数字圈(就像在老式的模拟表盘上以12的整数模一样)。为了使数值比较(x <y)有意义,我们需要确定哪些数字小于其他数字。从数学家的角度来看,我们希望以某种方式将256模整数嵌入所有整数的集合中。显而易见,将二进制表示形式为全零的8位整数映射为整数0。然后,我们可以映射其他对象,以使“ 0 + 1”(将寄存器清零,例如ax,并通过“ inc ax”将其递增1的结果)变为整数1,依此类推。我们可以对-1进行同样的操作,例如将“ 0-1”映射到整数-1和“ 0-1-1” 到整数-2。我们必须确保此嵌入是一个函数,因此不能将单个8位整数映射到两个整数。因此,这意味着如果我们将所有数字映射到整数集中,则将有0,还有一些小于0且大于0的整数。使用8位整数(根据到您想要的最小值(从0到-255)。然后可以根据“ 0 <y-x”来定义“ x <y”。
有两种常见的用例,对它们的硬件支持是明智的:一种使用所有非零整数都大于0的情况,一种使用约50/50围绕0的整数。通过使用额外的“加号”转换数字可以轻松地模拟所有其他可能性。和sub'之前的操作,对此的需求如此之少,以至于我无法想到现代软件中的一个明确示例(因为您可以使用更大的尾数(例如16位))。
另一个问题是将8位整数映射到16位整数空间中的问题。-1会变成-1吗?如果0xFF表示-1,这就是您想要的。在这种情况下,符号扩展是明智的选择,因此0xFF变为0xFFFF。另一方面,如果0xFF表示255,则希望将其映射到255,因此映射到0x00FF,而不是0xFFFF。
这也是“移位”和“算术移位”操作之间的区别。
但是,最终归结为以下事实:软件中的int不是整数,而是二进制的表示形式,并且只能表示某些形式。设计硬件时,必须选择在硬件中本机执行的操作。由于使用2的补码,加法和乘法运算是相同的,因此以这种方式表示负整数是有意义的。然后,仅取决于二进制表示要表示哪个整数的运算问题。
让我们检查一下将无符号整数添加到具有现有带符号整数的CPU设计的实现成本。
典型的CPU需要以下算术指令:
它还需要逻辑指令:
要对有符号整数比较执行上述分支,最简单的方法是让SUB指令设置以下标志:
然后算术分支的实现如下:
从这些实施方式的角度来看,显然应该否定它们。
因此,您现有的设计已经为带符号整数实现了所有这些功能。现在,考虑添加无符号整数所需要做的事情:
请注意,在每种情况下,修改都是非常简单的,并且可以通过打开或关闭一小部分电路,或者添加一个新的标志寄存器来实现,而该标志寄存器不能由需要计算的值控制。无论如何,指令的实现。
因此,添加未签名指令的成本非常小。至于为什么要这样做,请注意,内存地址(和数组中的偏移量)本质上是无符号的值。由于程序要花费大量时间来处理内存地址,因此具有正确处理它们的类型可以使程序更易于编写。
存在无符号数主要用于处理需要环绕的代数环的情况(对于16位无符号类型,它将是整数模65536的整数环)。取一个值,添加小于模量的任何数量,两个值之间的差即为所添加的数量。举一个实际的例子,如果一个公用事业电表在一个月初读数为9995,而一个人使用23个单位,则该电表在月底读数为0018。使用代数环类型时,无需执行任何特殊处理即可处理溢出。从0018减去9995将得到0023,恰好是使用的单位数。
在PDP-11(最初为其实现C的机器)上,没有无符号整数类型,但是有符号类型可用于模块化算术,该算法封装在32767和-32768之间,而不是在65535和0之间。但是,平台无法将内容包裹得很干净;而不是要求实现必须模拟PDP-11中使用的二进制补码整数,该语言改为添加了无符号类型,这些类型大多数必须表现为代数环,并允许有符号整数类型在发生溢出的情况下以其他方式运行。
在C的早期,有很多数量可以超过32767(公共INT_MAX),但不能超过65535(公共UINT_MAX)。因此,使用无符号类型来保存这样的数量(例如size_t)变得很普遍。不幸的是,语言中没有什么可以区分应该表现得像带正数位的数字的类型和应该表现出像代数环一样的类型。相反,该语言使小于“ int”的类型表现为数字,而全尺寸类型表现为代数环。因此,调用如下函数:
uint32_t mul(uint16_t a, uint16_t b) { return a*b; }
与(65535,65535)在int
具有16位(即返回1)的系统上具有一个已定义的行为,对于int
具有33位或更大(返回0xFFFE0001)的系统具有不同的行为,而在“ int”位于以下位置的系统上具有未定义的行为-之间(请注意,gcc 通常会产生算术上正确的结果,结果介于INT_MAX + 1u和UINT_MAX之间,但有时会为上述函数生成代码,但该值失败!)。不是很有帮助。
尽管如此,缺少始终像数字一样运行或始终像代数环那样运行的类型并不能改变代数环类型对于某些编程几乎不可缺少的事实。