原码、反码、补码

那天被同学问到了C++ Primer下面这一段话的含义:

就是将一个负数赋值给一个无符号数,会发生什么?

其实是一个很简单的问题,关键是书中的 “the remainder of the value modulo the number of values the target type can hold”引起了我理解上的歧义。

这里要先说说取模和取余的区别。详细可以参考这篇文章,取模和取余的不同,以及Stackoverflow的回答:Stackoverflow

取模和取余的区别关键点还是在于除法定义(整数相除结果要取整的情形)的不同,前者是商向无穷小方向取整,后者是商向零方向取整。

这样导致的结果是,对于商是正数的情况,两种除法定义的商相同,取模和取余的结果自然就相同。

但是当商是负数的情况下,两种除法定义的商就不相同了,然后取模和取余的结果就不同了。取模后的结果符号和除数一致,取余后的结果符号却和被除数一致。

在编程语言中,一般用 % 来表示取模或者取余,但是在一种语言中毫无疑问只能表示一种含义。

但是C++ primer中却这样写道:

我认为这是一种错误的说法,按照书中上下文的理解,在C++中,% 是表示取余的含义,而不能说成 “remainder” or “modulus”,一种语言中只能有一种含义,

而在Python语言中,% 的含义则是取模。

回头来看开头书中那一段话,蓦然发现居然是正确的。-1 mod 256 = 255,而原书中用的就是取模后的余数——“the remainder of the value modulo the number of values the target type can hold”。这也说明取模和取余是有区别的,而此处说成取模是正确的。

再来看最初的这一个问题,当把一个负数赋给一个无符号类型时,会发生什么?

当然很容易总结出来公式可以得到正确的值:例如,无符号类型的最大值 减去(所赋值的绝对值对无符号类型的最大值的余数)。

这个公式也适用于当一个无符号数和一个有符号数运算时,需要先将有符号数转化为无符号数。

但是这里面有一些更深一点的原因,就是以前我曾经了解过的原码、反码和补码的知识。

计算机中的数都是以补码的形式存在的,-1 的补码正是 11111111,赋给无符号数后,首位将不再是无符号数,所以正好是 255 的补码。

关于补码的理解,其实可以有两种方式:

(1)第一种就是按照大多数教材中所讲的原码-->反码-->补码的顺序来理解,先把第一位抽出来做符号位,0 表示正数,1 表示负数。

然后正数的反码和补码都等于其原码,而对于负数来讲,符号位不变,原码其余位取反为反码,反码再加一就是补码。

原码无法满足符号位参与运算,反码的存在可以解决符号位参与运算的问题,但是 0 却有正 0 和负 0 之分。

补码的出现不仅解决了符号位运算的问题,而且只用 00000000 表示 0,用 100000000来表示 - 256。这就可以比原码和反码多表示一个数。

因为 -256 并没有原码和反码,所以不能由其补码的形式来推断其原码还有反码。

参考这位知乎大神的回答:知乎

(2)第二种解释需要引入模的概念。

不需要从原码过渡到反码再过渡到补码,而是直接由模来引入补码的概念。

计算机中只有加法器,没有减法器。减法是通过加上减数相对于模的补数来实现的,而求一个数相对于模的补数在数字电路中也不需要减法。

正数和零的补码就是其本身,而负数的补码等于模减去其绝对值。

这样来看的话,其实可以说并没有符号位的概念,只是补码的定义恰好可以使计算机通过首位是 0 或 1 来判断是正数还是负数。

也就是说,第一位留出来作符号位并不是人为规定的,而是由补码的定义自然而然的结果。

参考:知乎原码、反码和补码

个人觉得,这两种理解都是可以的。

原文地址:https://www.cnblogs.com/niuxichuan/p/5917505.html