zoukankan      html  css  js  c++  java
  • Java编程的逻辑 (4)

    本系列文章经补充和完善,已修订整理成书《Java编程的逻辑》,由机械工业出版社华章分社出版,于2018年1月上市热销,读者好评如潮!各大网店和书店有售,欢迎购买,京东自营链接http://item.jd.com/12299018.html


    上节我们提到正整数相乘的结果居然出现了负数,要理解这个行为,我们需要看下整数在计算机内部的二进制表示。

    十进制

    要理解整数的二进制,我们先来看下熟悉的十进制。十进制是如此的熟悉,我们可能已忽略了它的含义。比如123,我们不假思索就知道它的值是多少。

    但其实123表示的1*(10^2) + 2*(10^1) + 3*(10^0),(10^2表示10的二次方),它表示的是各个位置数字含义之和,每个位置的数字含义与位置有关,从右向左,第一位乘以10的0次方, 即1,第二位乘以10的1次方,即10,第三位乘以10的2次方,即100,依次类推。

    换句话说,每个位置都有一个位权,从右到左,第一位为1,然后依次乘以10,即第二位为10,第三位为100,依次类推。

    正整数的二进制表示

    正整数的二进制表示与此类似, 只是在十进制中,每个位置可以有10个数字,从0到9,但在二进制中,每个位置只能是0或1。位权的概念是类似的,从右到左,第一位为1,然后依次乘以2,即第二位为2,第三位为4,依次类推。

    看一些数字的例子吧:

    二进制 十进制
    10 2
    11 3
    111 7
    1010 10

    负整数的二进制表示

    十进制的负数表示就是在前面加一个负数符号-,例如-123。但二进制如何表示负数呢?

    其实概念是类似的,二进制使用最高位表示符号位,用1表示负数,用0表示正数。

    但哪个是最高位呢?整数有四种类型,byte/short/int/long,分别占1/2/4/8个字节,即分别占8/16/32/64位,每种类型的符号位都是其最左边的一位。

    为方便举例,下面假定类型是byte,即从右到左的第8位表示符号位。

    但负数表示不是简单的将最高位变为1,比如说:

    • byte a = -1,如果只是将最高位变为1,二进制应该是10000001,但实际上,它应该是11111111。
    • byte a=-127,如果只是将最高位变为1,二进制应该是11111111,但实际上,它却应该是10000001。 

    和我们的直觉正好相反,这是什么表示法?这种表示法称为补码表示法,而符合我们直觉的表示称为原码表示法,补码表示就是在原码表示的基础上取反然后加1。取反就是将0变为1,1变为0。

    负数的二进制表示就是对应的正数的补码表示,比如说:

    • -1:1的原码表示是00000001,取反是11111110,然后再加1,就是11111111。
    • -2:2的原码表示是00000010,取反是11111101,然后再加1,就是11111110。
    • -127:127的原码表示是01111111,取反是10000000,然后再加1,就是10000001。 

    给定一个负数二进制表示,要想知道它的十进制值,可以采用相同的补码运算。比如:10010010,首先取反,变为01101101,然后加1,结果为01101110,它的十进制值为110,所以原值就是-110。直觉上,应该是先减1,然后再取反,但计算机只能做加法,而补码的一个良好特性就是,对负数的补码表示做补码运算就可以得到其对应整数的原码,正如十进制运算中负负得正一样。

    byte类型,正数最大表示是01111111,即127,负数最小表示(绝对值最大)是10000000,即-128,表示范围就是 -128到127。其他类型的整数也类似,负数能多表示一个数。

    负整数为什么采用补码呢?

    负整数为什么要采用这种奇怪的表示形式呢?原因是:只有这种形式,计算机才能实现正确的加减法。

    计算机其实只能做加法,1-1其实是1+(-1)。如果用原码表示,计算结果是不对的。比如说:

    1  -> 00000001
    -1 -> 10000001
    + ------------------
    -2 -> 10000010

    用符合直觉的原码表示,1-1的结果是-2。

    如果是补码表示:

    1  -> 00000001
    -1 -> 11111111
    + ------------------
    0  ->  00000000 

    结果是正确的。

    再比如,5-3:

    5  -> 00000101
    -3 -> 11111101
    + ------------------
    2  ->  00000010 

    结果也是正确的。

    就是这样的,看上去可能比较奇怪和难以理解,但这种表示其实是非常严谨和正确的,是不是很奇妙?

    理解了二进制加减法,我们就能理解为什么正数的运算结果可能出现负数了。当计算结果超出表示范围的时候,最高位往往是1,然后就会被看做负数。比如说,127+1:

    127   -> 01111111
    1     -> 00000001
    + ------------------
    -128  -> 10000000 

    计算结果超出了byte的表示范围,会被看做-128。

    十六进制

    二进制写起来太长,为了简化写法,可以将四个二进制位简化为一个0到15的数,10到15用字符A到F表示,这种表示方法称为16进制,如下所示:

    2进制 10进制 16进制
    1010 10 A
    1011 11 B
    1100 12 C
    1101 13 D
    1110 14 E
    1111 15 F

    可以用16进制直接写常量数字,在数字前面加0x即可。比如10进制的123,用16进制表示是0x7B,即123 = 7*16+11。给整数赋值或者进行运算的时候,都可以直接使用16进制,比如:

    int a = 0x7B;

    Java中不支持直接写二进制常量,比如,想写二进制形式的11001,Java中不能直接写,可以在前面补0,补足8位,为00011001,然后用16进制表示,即 0x19。

    查看整数的二进制和十六进制表示

    在Java中,可以方便的使用Integer和Long的方法查看整数的二进制和十六进制表示,例如:

    int a = 25;
    System.out.println(Integer.toBinaryString(a)); //二进制
    System.out.println(Integer.toHexString(a));  //十六进制
    System.out.println(Long.toBinaryString(a)); //二进制
    System.out.println(Long.toHexString(a));  //十六进制

    位运算

    位运算是将数据看做二进制,进行位级别的操作,Java不能单独表示一个位,但是可以用byte表示8位,可以用16进制写二进制常量。比如: 0010表示成16进制是 0x2, 110110表示成16进制是 0x36。

    位运算有移位运算和逻辑运算。

    移位有:

    • 左移:操作符为<<,向左移动,右边的低位补0,高位的就舍弃掉了,将二进制看做整数,左移1位就相当于乘以2。
    • 无符号右移:操作符为>>>,向右移动,右边的舍弃掉,左边补0。
    • 有符号右移:操作符为>>,向右移动,右边的舍弃掉,左边补什么取决于原来最高位是什么,原来是1就补1,原来是0就补0,将二进制看做整数,右移1位相当于除以2。

    例如:

    int a = 4; // 100
    a = a >> 2; // 001,等于1
    a = a << 3 // 1000,变为8

    逻辑运算有:

    • 按位与 &:两位都为1才为1
    • 按位或 |:只要有一位为1,就为1
    • 按位取反 ~: 1变为0,0变为1
    • 按位异或 ^ :相异为真,相同为假

    大部分都比较简单,就不详细说了。具体形式,例如:

    int a = ...; 
    a = a & 0x1 // 返回0或1,就是a最右边一位的值。
    a = a | 0x1 //不管a原来最右边一位是什么,都将设为1

    小结

    本节我们讨论了整数的二进制表示,需要注意的就是负数的二进制表示,以及计算机进行二进制加减操作的过程,从而我们就能理解为什么有的时候正整数计算会出现负数。

    我们同样讨论了整数的位运算,需要注意的就是无符号右移和有符号右移的区别。

    理解了整数,那小数呢?

    ---------------- 

    未完待续,查看最新文章,敬请关注微信公众号“老马说编程”(扫描下方二维码),深入浅出,老马和你一起探索Java编程及计算机技术的本质。原创文章,保留所有版权。

    -----------

    更多相关原创文章

    计算机程序的思维逻辑 (1) - 数据和变量

    计算机程序的思维逻辑 (2) - 赋值

    计算机程序的思维逻辑 (3) - 基本运算

    计算机程序的思维逻辑 (5) - 小数计算为什么会出错?

    计算机程序的思维逻辑 (6) - 如何从乱码中恢复 (上)?

    计算机程序的思维逻辑 (7) - 如何从乱码中恢复 (下)?

    计算机程序的思维逻辑 (8) - char的真正含义

  • 相关阅读:
    Encrypted Handshake Message
    RSAParameters Struct
    What if JWT is stolen?
    What's the difference between JWTs and Bearer Token?
    RSA Algorithm Example
    第18届Jolt大奖结果公布
    Ruby on rails开发从头来(windows)(三十六) 调试技巧
    Ruby on rails开发从头来(四十二) ActiveRecord基础(主键和ID)
    YouTube开放基础技术架构 让用户建自家YouTube
    Ruby on rails开发从头来(四十) ActiveRecord基础(Boolean属性)
  • 原文地址:https://www.cnblogs.com/swiftma/p/5409424.html
Copyright © 2011-2022 走看看