精华内容
下载资源
问答
  • 问题在进入正文之前,先提三个问题:计算机中的数为什么用补码(2's complement)来表示和存储?补码的计算规则是怎么来的?计算机是如何区分unsigned intint?众所周知,二进制是一种记数系统(类比十进制),而...

    上下文约束

    默认围绕8位计算机展开讨论。

    问题

    在进入正文之前,先提三个问题:

    1. 计算机中的数为什么用补码(2's complement)来表示和存储?
    2. 补码的计算规则是怎么来的?
    3. 计算机是如何区分unsigned int和int?

    众所周知,二进制是一种记数系统(类比十进制),而补码就是该系统之上的编码协议。协议是为了无序信息流变得规整,让人能够控制它。从这方面猜测,补码产生的原因是为了最小化硬件设计的成本,这大概也是最初的软件定义硬件(SDH)。

    当我们想象比特流的存储过程时,不免好奇自己头脑中的数值概念(尤其是负数和小数)怎么被计算机编码成有意义的比特流?这些比特流如何被正确地计算成另一种比特流?在更高层次上,编程语言中的short, int, unsigned int, long, long long等数值类型是怎样被计算机正确地识别的?

    通常,协议是处理复杂度的好方法,但隐藏在协议背后的原因比它本身更有探索的价值。如果你也感同身受,那么阅读下文就是合适的。

    为什么用补码?

    概念解释

    1. 原码[1](True form)
      原码是指一个二进制数左边加上符号位后所得到的码,且当二进制数大于0时,符号位为0;二进制数小于0时,符号位为1;二进制数等于0时,符号位可以为0(+0)或1(-0)。
    2. 反码[2](One's complement)
      反码是带有符号位的二进制数表示;负数的反码是将其对应正数按位取反,正数和0的反码就是该数字本身。
    3. 补码[3](Two's complement)
      补码也是带有符号位的二进制表示;负数的补码是将其对应正数按位取反再加1,正数和0的补码就是该数字本身。

    三者的关系很密切,是1对1的单射关系。准确地说,补码下可表示的最大负数没有对应的原码和反码。具体原因,待会儿做详细讨论。

    从实际应用的角度提问,现代计算机中数据的存储形式是原码、反码还是补码?这个问题其实可以规约到另一个问题上——哪种存储形式最有利于计算机进行运算?从这个角度思考就不难得出答案,当然是补码,不然,要是原码和反码都够用,为啥设计出补码?

    那么,为什么原码和反码不够用?单独从数据表示来看是无法得出结论的,需要从计算的角度思考。我们都知道,二进制是以2为基数的记数系统,和十进制、六十进制的记数本质相同。在最开始,记数系统中是没有负数的,引入负数是为了统一概念相反的事物。比如:收入和支出,支出就可以视为负的收入。如果用恒等式表达收支平衡收入 = 支出,将支出移项到左边就得变号收入-支出 = 0,换句话说,收入+(-支出) = 0,此时表达了总收入为0这个事实。相同的概念对于运算的意义重大,以前需要设计减法的运算规则,现在用加法规则就可以替代。在人类看来,这意味着概念的统一,而对于计算机而言,这简化了ALU(算术逻辑单元)中集成电路的设计。

    既然如此,负数在二进制中的表示就尤为重要。按照原码的定义,-1的二进制表示是1000 0001,那么计算1-1,也就是计算0000 0001 + 1000 0001 = 1000 0010,这个数表示的是十进制中的-2。1-1=-2当然是错的,它为什么是错的呢?因为符号位也参与运算了,上面的算式其实表达的是1+129=130这个算式。

    看到这里,或许需要思考的是数的表示和它们之间的运算是不对称的疑问?究其缘由是,数在计算机中表示是人为定义的,我们规定了左边的1是符号位,但是ALU不知道,而且也不应该知道。为什么呢?因为人是善变的,后面还会出现无符号数,那时候左边的1可就不能视作符号位。

    在继续探索之前,我们先补充点数学知识。

    同余

    当两个整数除以同一个正整数,若得相同余数,则这两个整数同余[4]。记为:

    读作a与b关于模m同余。同余的数具备很多性质,其中有一条保持基本运算:

    我们用这个数学性质来推导一下日常生活中的12小时制计时法。

    0点和12点关于12点同余,5和5当然关于12点同余,那么有:

    所以5和17关于12同余。当我们说下午5点时,其实也就是在说17点。负数也满足这样的关系,即-5和7关于12同余。

    负数的反码

    按照定义我们求得-1的反码,1000 0001的反码为1111 1110即254,-1和254相对于255(1111 1111)同余。依据同余的基本性质,

    取c为1,那么

    即0和255相对于255同余。当然,正确性显而易见。但是,这里面也出现了一个问题,0和255(-0)都是0这个数在反码中的正确表达,这也是负数的反码表示数的范围是[-127, 127],总数是255个的由来,就像12小时制计时0和12都是零点的表达,那么对于判0运算而言,就得有两手准备。简单起见,有没有其他方式去掉这种特殊情况呢?于是,补码顺势上位。

    负数的补码

    由于0和255都是8位计算机中0的合法表达,容易想到的一种方法就是去掉一个,而且还得保留同余的性质。

    假如把相对于255的同余变成256的同余是否有帮助呢?此时,0和256就相对于256同余了,但是因为8位二进制只能表示[0, 255]范围的数,再多就溢出了,256是没法表示的。我们还是以-1举例子:

    取c为1,那么

    0和256同余,即0000 00001 0000 0000同余,溢出位1被舍去,就变成了0000 0000

    负数的补码数的表示范围为[-128, 127],这个-128是怎么来的呢?它的补码表示是1000 0000。要解答这个问题,得看看原码和反码中的0的表示。

    先看看原码,左边第一位是符号位,说明它把0~255个数分成了两半,分别是0~127, 128~255。其中,0000 00001000 0000都表示0,所以它只能表示255个数。即,-127~127.

    类似地,反码中的0000 00001111 1111也都表示0,其中1000 0000表示-127,所以它也只能表示255个数。即,-127~127.

    但是补码就不同,它里面的0就只有一个0000 0000,而-128和128相对于256同余,但是128(

    )在8位机器上没法表示,所以干脆把
    1000 0000直接拿来当-128,可想而知,这个数是没法通过原码取反加1计算出来的,因为它对应的原码并不存在(整数128是不存在的)。

    补码的计算规则是怎么来的?

    正数的补码就是其本身;
    负数的补码是在其原码的基础上, 符号位不变, 其余各位取反, 最后+1

    先说说反码计算方式的由来。以-1为例,其原码表示如下:

    1000 0001

    那么为何对它符号位之外进行取反,就得到了它相对于255(1111 1111)的反码呢?

    我们用255减去-1的绝对值,试试看

    1111 1111
    - 0000 0001
    -----------
      1111 1110

    1111 1110就是254,它和1000 0001保留符号位后各位取反的结果一致。这是巧合吗?显然不是,因为1111 1111是8位中最大的数,所以就不存在借位操作,那么各位减下来,位是0的自然得到1,位是1的自然得到0.

    -1和254相对于255同余,所以-1是254的补(One's complement)。不信的话,可以去clojure repl中运行下面的表达式。

    (mod -1 255) #=> 254

    知道到了反码的求值方法的由来,我们不难推断出补码的计算方法。因为256是255+1,取反就是用255减去该数,那么用256减去该数,也就等价于255减去该数再加一。

    利用这个知识点,我们推导下早期的的计算机中使用的加三码(excess-3),这种编码方式是利用了4位来表示数字0-9。

    0 -> 0011
    1 -> 0100
    2 -> 0101
    ...
    8 -> 1011
    9 -> 1100

    这种表示有什么好处呢?加三码中针对0-9中任意一个数x,9减去x得到的数的表示和x的各位取反之后的数是相同的。因为这样的特征,设计减法电路就非常方便了。

    9-1=8
    1 -> 0100
    取反 -> 1011
    即:8 -> 1011

    当然这也不是巧合。理由很简单,4位最大表示数是15,所以9-x可以写成下面的等式

    1100-x+0011 = 1111-x

    所以等同于对x的取反。

    计算机是如何区分unsigned int和int的?

    我们已经知道了计算机存储数据全部用的补码的形式,所以从内存中拿出来的数就是补码,那么-1的补码是1111 1111,也就是数2^8-1=255.

    如果说unsigned int的存储形式和int的一致,那意味着int负数转换成unsigned int的值就是它补码的字面值。我们使用Solidity语言程序验证,在验证之前,先要安装ganache-cli和solidity-repl.

    $ ganache-cli
    ...
    $ solr
    Welcome to the Solidity REPL!
    > int8 c = -1
    > uint8 d = uint8(c)
    > d
    255

    -1的补码是1111 1111,也就是十进制的255,所以从结果中不难得出如下结论:在计算机中,数的存储和表示是分开的,存储的是补码,计算过程也使用补码,但是最后的表示由程序员来决定。所以毫不夸张地说,程序员是规则的缔造者,也是规则的解读者。


    顺带一提
    solidity中的int和uint是成对的,而且从8, 16, 24, ..., 256,一共有32个。正确性可以通过它的词法分析程序得出来。

    //solidity/liblangutil/Token.cpp
    
    if (0 < m && m <= 256 && m % 8 == 0 && positionX == _literal.end())
    {
        if (keyword == Token::UInt)
            return make_tuple(Token::UIntM, m, 0);
        else
            return make_tuple(Token::IntM, m, 0);
    }
    展开全文
  • 今天我们讨论一个最基础的问题:进制。我们平常使用的自然数是十...关于数的进位制,例如N位制,常见的一种观点是,低位满N,就向高位进一。除了这种理解之外,我们这里再讨论一种更有用的理解。那就是将数字的每一...

    今天我们讨论一个最基础的问题:进制。

    我们平常使用的自然数是十进制的,在计算机中使用的是二进制运算,使用二进制是因为电子元器件只能用高低电压两种状态来进行组合和计算,这就决定了二进制是现代计算机的基础。为了方便表示,人们还经常会使用八进制和十六进制来记录二进制数。关于数的进位制,例如N位制,常见的一种观点是,低位满N,就向高位进一。除了这种理解之外,我们这里再讨论一种更有用的理解。那就是将数字的每一位赋予一个权重,一个数字的真实值就是每一位上的数字乘以这一位所对应的权重,然后再加起来。例如,对于一个十进制数,个数的权重是1 = 10^0,十位的权重是10^1,百位的权重是10^2,依次类推,那么,一个十进制数1368就可以表示为

    bede624ac49cd36f5740bf473abb97ba.png

    同样,一个二进制数可以表达为

    44f66434389e040934b269c97662386f.png

    这个分解式看起来很简单,但意义却很重大,这使得我们从“满几进一”的思维中跳出来,可以重新定义每个位的权重。比如在普通的十进制数中,十位的权重是10,百位的权重是100,但是,我们可以根据需要,规定十位是十进制,百位是八进制的,也就是说,十位的权重仍然是10,百位的权重变成了8∗10 = 80。(这句话务必看明白,我们创造了新的混合进制数。在后面的文章里我会介绍递增进位制,这种进位制的每一位都不相同,但它却是编码状态,进行组合计数等等的基础,如果不理解进位制的话,那么这种技巧在你看来可能就是很难想到的淫巧奇技,但是在进制分析法面前,这种技巧毫无秘密可言。)

    79f2a7ec42bfe11b817bda8fb2e33d32.png

    计算机中数字的表示

    计算机中是如何表示一个整数的,一个负数呢?一个浮点数呢?搞清楚这个问题有多重要,我来举个例子。曾经有一位网络游戏的服务端同学,在写角色身上的金钱的时候,使用了int型,但在程序的其他地方,却又使用了unsign int来存储,更不幸的是,在扣玩家身上的金钱的地方,又出现了并发的情况,导致金钱会出现负数。一个整型负数,转成无符号整型的时候,悲剧就发生了:玩家变得巨有钱。一个刷金的BUG就这样产生了。要搞明白这个问题,就得先搞明白,在计算机中,每个数是怎么表示的。还有,比如,这样一个BUG,有一次,我发现从网络接收的数据不对,只好把接到的数字一个字节一个字节地去分析,最后发现,发送方发送了一个浮点型,而接受方却是以整型去解释的。那么浮点型的1.0和整数的1到底有什么不同呢?(这还是原始时代的BUG,感谢protobuf,netty,我们再也不用担心应用层网络消息的格式了。)

    我们都知道Java中的byte类型是一个字节,8位,short是两个字节,16位,int是四个字节,32位,long是8字节,64位。为了方便描述,我们以byte为例,一个字节,8位能表达的范围是从(00000000)2 ~ (11111111)2,转成十进制,就是从0到255。但书上告诉我们,byte能表达的范围是-128~127。这就牵扯到了负数的表达了。在计算机里,对于有符号的数据类型,会把最高位做为符号位,如果为1,则为负数。我们可以测试一下。

    int i = 1;Assert.assertTrue((a << 30) > 0);Assert.assertTrue((a << 31) < 0);Assert.assertEquals((a << 31), Integer.MIN_VALUE);

    看,如果我们把1左移31位,也就是送到了最高位上,这个数就变成了int型所能表示的最小值了。

    由于32位整数写起来太罗嗦了,我们先只以byte的情况进行分析。负数通常有三种表示方法。第一种,把符号位设为1,后面的数字等于这个负数的绝对值,例如-15就变成了10001111。这种方法称为原码。还有一种,保持符号位不变,后面的数字全部取反,例如-15就变成了11110000,这种方法称为反码。最后一种,称为补码,它是由反码加1得到的。例如,-1的反码是11111110,再加1就是11111111。使用补码的好处包括,0和-0的表示是相同的,正负数可以直接进行加减法运算,不必再做额外的转换。而原码和反码则不具备这样的优点。所以现代计算机都是使用补码来表示负数。

    递增进位制数

    递增进位制数是这样一种数,它的最右一位(称为个位已经不太准确了,我们使用左右来代替个位十位的称呼)是二进制的,它的右数第二位是三进制的,右数第三位是四进制的 ……它的右数第N是N + 1进制的。用权重的观点来看,它的最右一位的权重是1,右数第二位的权重是1∗2 = 2!,右数第三位的权重是1∗2∗3 = 3!,右数第N的权重是N!。 我们也用上面的方法来表示递增进位制数:

    0ad838ea85b448bfd0caa2e509f3c238.png

    递增进位制数的最常见的一个作用就是用来编码全排列。通过递增进位制数,我们可以建立N个元素的全排列与自然数[1,N!]的一一对应关系。在下一节,生成全排列那里,我们会进行详细地讲解。

    095e1bfd80fe62ac3ad9f853e09245f3.png

    好了,看完上面的内容,可以尝试实现下下面的题目:

    1. 写一个程序,输入是一个十进制整数,输出是它的二进制表示的字符串。函数原型 String oct2Bin(int a),例如,输入8,返回值是“1000”。要考虑负数哦。
    2. 写一个程序,进行数制的相互转换,原型是String transform(String s, int radixSrc, int radixTgt),其中,s代表原始的字符串,radixA代表源进制,radixB代表目标进制。例如transform("221
    展开全文
  • 虽然本文是针对零基础同学,但我相信,即使是写过一段时间程序“老人”,读一读这篇文章内容,肯定也是有好处。专栏共设了52周课程。课程内容都会发表在知乎专栏。好了,下面正式开课。我课程不会是速成...

    虽然本文是针对零基础的同学,但我相信,即使是写过一段时间程序的“老人”,读一读这篇文章的内容,肯定也是有好处的。

    专栏共设了52周的课程。课程内容都会发表在知乎专栏。好了,下面正式开课。

    我的课程不会是速成的课程,而是会从最基础的东西开始讲,让大家认清程序的本质。从而不可能达到XX天学会某某这种效果。这一定是一个漫长而痛苦的过程,如果希望我有什么独门秘技可以速成的,您就可以关上页面了。我这里讲的东西,可能无法马上反应在你的程序里。但却是打好基础所必需的。

    今天我们讨论一个最基础的问题:进制。

    我们平常使用的自然数是十进制的,在计算机中使用的是二进制运算,使用二进制是因为电子元器件只能用高低电压两种状态来进行组合和计算,这就决定了二进制是现代计算机的基础。为了方便表示,人们还经常会使用八进制和十六进制来记录二进制数。关于数的进位制,例如N位制,常见的一种观点是,低位满N,就向高位进一。除了这种理解之外,我们这里再讨论一种更有用的理解。那就是将数字的每一位赋予一个权重,一个数字的真实值就是每一位上的数字乘以这一位所对应的权重,然后再加起来。例如,对于一个十进制数,个数的权重是1 = 10^0,十位的权重是10^1,百位的权重是10^2,依次类推,那么,一个十进制数1368就可以表示为

    同样,一个二进制数

    也可以表达成

    这个分解式看起来很简单,但意义却很重大,这使得我们从“满几进一”的思维中跳出来,可以重新定义每个位的权重。比如在普通的十进制数中,十位的权重是10,百位的权重是100,但是,我们可以根据需要,规定十位是十进制,百位是八进制的,也就是说,十位的权重仍然是10,百位的权重变成了8∗10 = 80。(这句话务必看明白,我们创造了新的混合进制数。在后面的课程里我会介绍递增进位制,这种进位制的每一位都不相同,但它却是编码状态,进行组合计数等等的基础,如果不理解进位制的话,那么这种技巧在你看来可能就是很难想到的淫巧奇技,但是在进制分析法面前,这种技巧毫无秘密可言。看不明白的,在小密圈里提问,我会及时解答)

    计算机中数字的表示

    计算机中是如何表示一个整数的,一个负数呢?一个浮点数呢?搞清楚这个问题有多重要,我来举个例子。曾经有一位网络游戏的服务端同学,在写角色身上的金钱的时候,使用了int型,但在程序的其他地方,却又使用了unsign int来存储,更不幸的是,在扣玩家身上的金钱的地方,又出现了并发的情况,导致金钱会出现负数。一个整型负数,转成无符号整型的时候,悲剧就发生了:玩家变得巨有钱。一个刷金的BUG就这样产生了。要搞明白这个问题,就得先搞明白,在计算机中,每个数是怎么表示的。还有,比如,这样一个BUG,有一次,我发现从网络接收的数据不对,只好把接到的数字一个字节一个字节地去分析,最后发现,发送方发送了一个浮点型,而接受方却是以整型去解释的。那么浮点型的1.0和整数的1到底有什么不同呢?(这还是原始时代的BUG,感谢protobuf,netty,我们再也不用担心应用层网络消息的格式了。)

    我们都知道Java中的byte类型是一个字节,8位,short是两个字节,16位,int是四个字节,32位,long是8字节,64位。为了方便描述,我们以byte为例,一个字节,8位能表达的范围是从(00000000)2 ~ (11111111)2,转成十进制,就是从0到255。但书上告诉我们,byte能表达的范围是-128~127。这就牵扯到了负数的表达了。在计算机里,对于有符号的数据类型,会把最高位做为符号位,如果为1,则为负数。我们可以测试一下。

    int 

    看,如果我们把1左移31位,也就是送到了最高位上,这个数就变成了int型所能表示的最小值了。

    由于32位整数写起来太罗嗦了,我们先只以byte的情况进行分析。负数通常有三种表示方法。第一种,把符号位设为1,后面的数字等于这个负数的绝对值,例如-15就变成了10001111。这种方法称为原码。还有一种,保持符号位不变,后面的数字全部取反,例如-15就变成了11110000,这种方法称为反码。最后一种,称为补码,它是由反码加1得到的。例如,-1的反码是11111110,再加1就是11111111。使用补码的好处包括,0和-0的表示是相同的,正负数可以直接进行加减法运算,不必再做额外的转换。而原码和反码则不具备这样的优点。所以现代计算机都是使用补码来表示负数。

    递增进位制数

    递增进位制数是这样一种数,它的最右一位(称为个位已经不太准确了,我们使用左右来代替个位十位的称呼)是二进制的,它的右数第二位是三进制的,右数第三位是四进制的 ……它的右数第N是N + 1进制的。用权重的观点来看,它的最右一位的权重是1,右数第二位的权重是1∗2 = 2!,右数第三位的权重是1∗2∗3 = 3!,右数第N的权重是N!。 我们也用上面的方法来表示递增进位制数:

    递增进位制数的最常见的一个作用就是用来编码全排列。通过递增进位制数,我们可以建立N个元素的全排列与自然数[1,N!]的一一对应关系。在下一节,生成全排列那里,我们会进行详细地讲解。

    好了,第一天,不讲太多了吧。今天的作业:

    1、写一个程序,输入是一个十进制整数,输出是它的二进制表示的字符串。函数原型是

    String oct2Bin(int a)

    例如,输入8,返回值是“1000”。要考虑负数哦。

    2、写一个程序,进行数制的相互转换,原型是

    String transform(String s, int radixSrc, int radixTgt)

    其中,s代表原始的字符串,radixA代表源进制,radixB代表目标进制。例如

    transform("221", 3, 2),返回“11001”。因为3进制的221,就是十进制的25,而十进制的25就是二进制的11001。同理,transform("11001", 2, 3)返回值就是“221”。

    3、猜想一下以下代码的运行结果是什么?并写程序验证,体验一下数据溢出。

    short a = Byte.MIN_VALUE;
    int b = Short.MIN_VALUE;
    short c = Byte.MAX_VALUE + 1;
    byte d = (byte)(Byte.MAX_VALUE + 1);

    4、自己查一下float和double在计算机内是以什么格式输出的,并思考,如果在网络上只能以字节流的方式传送,这些数字应该怎么办?(例如,InputStream/OutputStream都只能处理字节流。)

    展开全文
  • 我们在Yannick:Introduction to CSAPP(二)信息的表示与处理·概述中说到:确定一个数值数据值,必须确定:进位计数制它表示二进制数据,还是一个十进制数据?定点、浮点表示这个数据是整数,还是小数,因为在...

    我们在Yannick:Introduction to CSAPP(二)信息的表示与处理·概述中说到:

    确定一个数值数据的值,必须确定:

    • 进位计数制
      它表示二进制数据,还是一个十进制数据?
    • 定点、浮点表示
      这个数据是整数,还是小数,因为在计算机中无法表示小数点,只能约定某一位开始表示小数部分。整数和小数在字面看起来是极其类似的。
    • 二进制编码形式
      是正数还是负数?因为在计算机中无法表示正负号,只能约定某一位正负。

    这篇文章讲的就是,第二点中的定点数表示。

    结合本文内容,可以解释Introduction to CSAPP(二)信息的表示与处理·概述 - Yannick的文章 - 知乎中,引子提出来的两个问题。让我们来看看吧~


    什么是定点数、浮点数?

    定点数、浮点数是计算机中小数的表示方法。比如我们规定一个八位的二进制序列,第五位极其后的位表示小数部分。那么

    d9340d0488574107a544f5c89b56d3a0.png
    按照上图,我们就可以计算出来这个二进制序列所表示的十进制真值

    这种表示方式就是定点数,它可以表示的数字范围是有限的。不确定小数点的数值表示法,叫浮点数。

    编程语言中的整数

    在计算机中,整数用小数点在最末位数后的定点数来表示。以C语言支持的基本数据类型为例:

    d7e1a79d879820f6f74183a4e46ee6b6.png
    32位机上的典型取值

    ea495a2340e797caf46cef52969356ed.png
    64位机上的典型取值

    但是这个整数的数值在C语言中并不绝对,在涉及到跨平台的问题时,不同的操作系统为基本的数据类型分配的大小时不一样的,这个涉及到操作系统选择的C数据模型。

    下表 32 位和 64 位数据模型

    加粗的为不同模型下表现一致的。

    ILP32 => define int, long, and pointer as 32bits
    LP64 => define long and pointer as 64bits
    LLP64 => define long long and pointer as 64 bits
    ILP64 => define int, long and pointer as 64 bits

    9b3aa02b34e72ead383456126641d4c1.png
    操作系统与C data model 对应关系
    在32位的操作系统下,大部分的操作系统都是采用的ILP32,因此,long是4个字节
    在64位的操作系统下,Windows采用的是LLP64,long是4个字节,类Unix操作基本都是采用的LP64,因此,long是8个字节

    Java中整数的数据类型如下表

    c91b5e877df18c5235bfc4898cafd6df.png

    对于Java来说,没有无符号数,具体的Java基本数据类型见这里。

    整数的表示与类型转换

    对于C语言来说,C语言标准没有指定有符号数在内存里要采取哪种表示方式,但是基本上所有的机器都采用补码表示。对于无符号数,无论用原码、补码还是反码,它都是一致的。

    对于Java语言来说,整数数据类型的表示是补码,注意Java是没有符号概念的,reference。

    在 C 语言中,如果不加关键字限制,默认的整型是有符号的。如果想要无符号数的话,需要在数字后面加 U或者u,例如下面的代码段

     int a_signed_number = -15213;
     unsigned int a_unsigned_number = 15213U;
    • 具体每一个字节的内存表示值不会改变,改变的是计算机解释当前值的方式
    • 如果一个表达式既包含有符号数也包含无符号数,那么会被隐式转换成无符号数进行比较

    在进行有符号和无符号数的互相转换时:

    f7ef33025ba07545f5c58445cd218fb0.png
    注意表中标注出的例子,他们和预想的表示不一致。

    我们在数轴上把有符号数和无符号数画出来的话,就能很清晰的看出相对的关系:

    214531a6cf2b9ae3342fb6e4109e4cba.png
    Reference: https://wdxtub.com/csapp/thin-csapp-1/2016/04/16/

    整数运算与溢出

    • 无符号数加法
      如果两个 w 位的数字相加,结果是 w+1 位的话,那么就会丢弃掉最高位,实际上是做了一个 mod 操作(公式为 )
    • 有符号数加法
      操作过程和无符号加法一样,只是解释的时候会有不同,因此会得到正溢出(positive overflow)和负溢出(negative overflow)两种。
      • 正溢出就是数值太大把原来为 0 的符号位修改成了 1,反而成了负数;
      • 负溢出是数值太小,把原来为 1 的符号位修改成了 0,反而成了正数。

    本文介绍了在计算机中,整数的表示方式。在计算机中,整数是以补码的方式表示而存储的,知道这一点,在使用位运算时才能知道自己做了什么。
    同时到了这里我们也就解释了Introduction to CSAPP(二)信息的表示与处理·概述 - Yannick的文章 - 知乎中,引子提出来的两个问题。具体解释可以见下篇文章。
    展开全文
  • 我们会研究扩展或者收缩一个已编码整数以适应不同长度表示的效果。2.2.1 整数数据类型C语言支持多种整型数据类型------表示有限范围内的整数。这些类型如图2-8和2-9所示,其中还给出了“典型的”32位和64位机器的...
  • 虚拟内存虚拟内存是一种计算机内存管理技术,,它将程序使用内存地址映射成内存中物理地址(使应用程序认为它拥有大型连续可用地址空间,实际上是被分隔多个物理内存碎片,有一部分甚至没有在内存上,而是...
  • 作者 | channingbreeze来源 | 互联网侦察声明:已获作者授权转载小史是一个应届生,虽然学是电子专业,但是自己业余时间看了很多互联网与编程方面书,一心想进BAT。今天他就去BAT中一家面试了。简单自我介绍...
  • 转眼之间初中毕业30年了,但我仍清楚记得初中英语一篇课文,题目叫《皇帝新装》(“The king’s new clothes”)。这篇课文前两句话是:”Long long ago, there was a king. He liked new clothes.“ 因为整篇...
  • 前面几节较为详细讨论了 linux 内核中“链表”和“队列”数据类型,本节再来说说中映射(idr)机制。先来说说映射适合用于解决什么问题linux 系统中许多资源都用整数 ID 标识,例如进程 PID,文件描述符 ID ...
  • int 整形能表示的最大范围

    千次阅读 2010-04-26 19:53:00
    #include #include using namespace std;const int MIN=1const int MAX=(1int main (){ cout " cout " cout cout::max() cout::max() cout::ma
  • 无符号时:unsigned int能表示范围: 0~4294967295 由上可知:int作为有符号能表示的正数数据范围最大应该超过20亿了 当然,longlong为64位的 可以自己打印试一下: #include "pch.h" #include<iostream>...
  • 从上文可以看出,每个数据类型都有其所能表示的范围,超过这个范围就会出一些莫名其妙的错误 上文网址:http://blog.csdn.net/shanliangliuxing/article/details/7394319 比方说两个int型数据相加时就有可能出现...
  • 一个 int 占4个字节,就是32个比特位,所以能表示的范围为-2^31~+2^31-1 2,147,...若是unsigned int,能表示的范围是0 ~ +2^32-1 4,294,967,295 约43亿,与地球人口还差那么一大段距离(约78亿) 看一段源码:
  • 要注意int的数字范围

    2017-10-01 16:55:13
    以下这个程序运行时屏幕输出的数字会无线增大 #include"stdio.h" int main() { int a=2; int t=0; ...因为4294967295已经超出了int能表示的范围。把判断条件改小就行 如果把程序改成这样#inc
  • int能表示的最大整数

    千次阅读 2019-04-02 02:16:28
    在算法竞赛中,应该对常用数据类型,比如int,longlong,所能够表示的最大范围有所了解。 比如最简单的求两个数的和,输入a,b,输出a+b。但如果a和b的位数分别都超过20位应该怎么做呢? 预储备知识:你需要有...
  • int 的范围

    万次阅读 2018-04-14 23:55:16
    int 十进制:-2^31=-21 4748 3648 到...(一个字节,占8位)int能表示的最大正整数为:0111 1111 1111 1111 1111 1111 1111 1111 (最高位表示符号位,正数符号位为0)对应的10进制为2^31-1=2147483647,对应的十六...
  • java中int 类型的范围是:-2147483648 到2147483648 那他能表示最大 ... bbs.csdn.net › CSDN论坛 › Java › Java SE 2007年10月3日 - int 类型的范围是 -2147483648...2147483647 因为int所...
  • C++ 整型所能表示的数据范围

    万次阅读 2017-07-06 10:22:29
    C/C++中,整型数据是用int来进行定义的... int能表示的最大正整数为:0111 1111 1111 1111 1111 1111 1111 1111 (最高位表示符号位,正数符号位为0)对应的10进制为2^31-1=2147483647,对应的十六进制表示为:0x7FFFF
  • 本文复制自https://blog.csdn.net/sinat_33718563/article/details/74520071,是为了自己以后查看方便才复制过来的,谢谢原博主。 C/C++中,整型数据是用int来... int能表示的最大正整数为:0111 1111 1111 111...
  • 几种int类型的范围

    2017-05-29 23:11:00
    我们在编程的过程经常会遇到数据溢出的情况,于是这个时候我们必须定义能表示更大的数的数据类型来表示这个数。 下面列出了int的范围: unsigned int 0~4294967295 int -2147483648~2147483647 unsigned ...
  • int 类型数值范围

    2014-03-25 18:42:28
    [size=large]int 类型的范围是 -2147483648...2147483647(-2^31 ...对于正数,它的最高位是0,所以它最大能表示的数是01111....(后面共31个1,2^31-1)而这个数化成十进制即是2147483647.对于负数也是同理。 [/size]...
  • C/C++中,整型数据是用int来进行定义的... int能表示的最大正整数为:0111 1111 1111 1111 1111 1111 1111 1111 (最高位表示符号位,正数符号位为0)对应的10进制为2^31-1=2147483647,对应的十六进制表示为:0x...
  • C++int的数到底最大值是多少

    千次阅读 2020-07-31 21:56:49
    为什么正数要31-1呢:个人理解:(假设4个1,如果直接2次方是16,但它不能表示16,16是范围的意思,它意思是能表示16个:0~15吧),所有31次方就要-1啦。 负数为什么不要-1呢(不太理解计组知识,懂大佬告诉...
  •  32位RAM处理器中,int代表4个字节,具有32位,它的范围是-2147483648~2147483647的整数。  若想对全范围整数进行查重,即对于任意数量任意大小的int变量I,判断I是否出现在已知的数组中。若通过整数int[]来...
  • 溢出和取值范围C语言的整型溢出问题 整数溢出 int、...double与int类型的存储机制不同,long int的8个字节全部都是数据位,而double是以尾数,底数,指数的形式表示的,类似科学计数法,因此double比int能表示的数...
  • 对于有符号的int型整数来说,它能表示的范围是: [−231,231−1][−231,231−1] 但是我们不能简单地用计算机算出具体的数字直接接进行比较;更好的方法是使用C++预置的边界进行判断,他们分别是:INT_MAXINT_MIN ...
  • |我建议你去看看 最基本的 计算机存储方式 |||与编译器有关了 超出范围 因为此时计算机无法表示 -32 768~32 767这个范围是十进制数的范围 16只是2进制数的范围 16跟-32 768~32 767这个没关系 |||取值范围 不超过...
  • int是C++关键字,表示整型,其大小是32位有符号整型,表示的范围是-2,147,483,648 到 2,147,483,647;在声明和定义变量时使用,它表示的意思是所声明或所定义的变量为整型变量。如果其用于函数参数时,其传递方向为...
  • char型数据里面数据是以16进制存储,每个char型数据可能值是从0x00~0xff(表示为2进制范围是从00000000~11111111)16进制,即每个char型数据里面存储8位数据。如果存储类型为BCD码,那也就是按照2进制...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 814
精华内容 325
关键字:

int能表示的数的范围