double取整数部分_数据类型专题之一: 整数类型和补码

写给读者中的初学者:数据类型是小白初学编程的一个小难点,关于补码的介绍可能稍微有点硬核,但是这将成为你理解位运算的基础,同时也会丰富你对程序世界的认知,认真读懂会大有裨益。

概述

数据类型指的是变量的类型

Java包含8种基本的数据类型。

整数类型:int (整型), long(长整型), short(短整型), byte (字节)

浮点数(小数): float(单精度浮点类型), double(双精度浮点类型)

字符类型:char

布尔量:boolean

3d242c2044c2c30a0fb498d1ff40a192.png

作为一门静态语言,Java属于强类型语言。Java中的每种基本类型占据确定的储存空间,而不像其它大多数语言那样随机器硬件架构的变化而变化,这一特性使得Java相比其他语言更具移植性,而这一切得感谢JVM。

数据类型占用的空间以 bit 为单位。一个 bit 就是一个晶体管所能代表的信息量,即 0 或 1。一个 bit 也其实就是一个 2 进制位。

程序运行时的变量的值存在于内存(memory)之中。


整数

int

当你需要表示一个整数时,一般创建一个 int 型变量即可。int类型的变量占 32 个bits的空间。

从数理的角度说,整数没有上下限,但是在计算机中,int型的范围是 [-2^31, 2^31-1],即 [-2147483648,2147483647] 。想象一下,现在有内存中的 32 个 晶体管排列在你面前,然后我们定义的int num1 = 2;,num1 变量在这32个晶体管中将会这样存在 (0和1表示晶体管的两种电荷分布状态) :

0000 0000 0000 0000 0000 0000 0000 0010

如果给num1赋值 num1 = 1000;

0000 0000 0000 0000 0000 0000 0110 0100

其实就是一个简单的二进制换算后再补齐32位,我想通过这个具象说明的是:不管一个变量储存的值是多少,变量占用的储存空间是不变的。

相信现在你可以得心应手得用32个晶体管表示很多自然数了,那么加一点难度,num1 = -2;,如何用32个晶体管表示 ?

卡住了对吗,因为负数在现实世界中不存在。也许我们得付出一个bit的空间来表示这个数是正数还是负数,例如,第一个晶体管是 1 时就表示后面的31个晶体管表示的数字是负数,是 0 时就表示是正数, 那么现在表示 -4(由于大小端模式,实际的排列情况于此处不同,此处仅做演示之用):

1000 0000 0000 0000 0000 0000 0000 0010

看起来完美了,我们用剩下的31个晶体管可以表示[-2^31+1, 2^31-1]之间的所有整数,这和刚才给出的 int 的范围 [-2^31, 2^31-1] 很接近了,但是现在考虑一下这个:

int num1 = -2 + 2;
0000 0000 0000 0000 0000 0000 0000 0010      // 2
        +
1000 0000 0000 0000 0000 0000 0000 0010   // -2
        = 
1000 0000 0000 0000 0000 0000 0000 0100   // -4

依据我们刚才定下的规则 -2 + 2 = -4 ,这显然违背了基本的数学规律,另外一个问题是,这种表示规则还导致了正0和负0的出现:

1000 0000 0000 0000 0000 0000 0000 0000
0000 0000 0000 0000 0000 0000 0000 0000

两种 0 的出现会导致像 num1 > 0 这样的比较的结果出现问题。当 num1 为 正0 而大于号右侧为负0 时,就会出现 0 > 0 的结果为 true 的谬误。更糟糕的是,-1的二进制表示在增加1之后是-0,而-0无法通过增加1变为+1,正数和负数之间出现了一条鸿沟。

如果我们真的应用了这种编码方式,计算机必须被设计为花费很多性能去纠正物理层面和数理不符的部分。事实上,这种数据储存方式在历史上被称为原码,而原码之后的反码因为无法统一正0和负0也被淘汰了,如今运行在你的计算中的数据以补码的形式存在

为了追求计算机在物理层面和数理的统一,人们通过组合各式电路逻辑门统一了物理层的二进制运算和数学上的二进制加法 (加法器是计算机的运算核心),而补码的出现彻底统一了计算机的物理层和数理,完美的解决了正数和负数相加的问题。

依然是32个晶体管,我们继续表示+2

0000 0000 0000 0000 0000 0000 0000 0010

而在补码系统中,一个数和它的相反数的关系是这样的: 一个数 (二进制表示) 对每个bit取反之后,再加上1就得到其相反数,根据这个规律,-4应当这样表示。

1111 1111 1111 1111 1111 1111 1111 1110

现在在再运算-2+2(代码框可以左右拖动哦)

1111 1111 1111 1111 1111 1111 1111 1110
        +
0000 0000 0000 0000 0000 0000 0000 0010
        =
(1) 0000 0000 0000 0000 0000 0000 0000 0000    

由于我们只有32个晶体管,最高位的 1 事实上被舍去了,因此结果为 0。至此补码解决了正负数相加的问题,而补码的另一个优越性在于,没有两种 0 的存在,正数和负数之间是连贯的。

1111 1111 1111 1111 1111 1111 1111 1111    // -1 
          +
0000 0000 0000 0000 0000 0000 0000 0001    //  1
          =
(1) 0000 0000 0000 0000 0000 0000 0000 0000    //  0

补码表示的-1可以通过两次物理层的加法器加1 达到+1,消灭了计算机系统中0的二义性,彻底统一了计算机运算的物理层和数理。但是因此1000 0000 0000 0000 0000 0000 0000 0000这个物理状态被闲置了,于是人们人为规定这个物理状态用来代表32位二进制位能表示的最小值再减一,这就是为什么int类型的最小值的绝对值比最大值大1。

long

有了上面int类型的基础,再去理解其他整数类型就轻松多了,其底层都是补码。

long类型被设计为占据64 bits,因此long类型的范围是[-2^63, 2^63-1]。

需要注意以的一点是,任何字面上出现在Java源代码中的数字常量都默认为十进制的int类型,long类型的字面量必须在数字结尾加L

long l = 10000;    //这样写存在类型转换,因为1000这个字面量是int类型的。
long l = 10000L;   //在结尾加上L后,1000这个数字常量才是long类型。

short  和 byte

short类型占16 bits,范围为[-2^15, 2^15-1], byte类型占 8 bits,范围为[-2^7, 2^7-1]。

表示非十进制整数

有时我们会需要直接给变量赋非十进制表示的值或者使用非十进制的数字常量。在Java语言中,除了可以用十进制的形式表示整数以外,还可以用二进制、八进制和十六进制的方式表示整数。如果用十六进制的形式表示一个整数,数字要以0X开头,如果用八进制表示,则用0开头,而用二进制表示,用0B开头。

byte n1 = 0B1011; //二进制表示法,换算等于十进制的11

int n2 = 072; //八进制表示法,换算等于十进制的58

long n3 = 0XFF5C; //十六进制表示法,换算等于十进制的65372

数据类型的整数类型讲到这就差不多结束了,受限于篇幅原因,数据类型的内容没法一次讲完,暂时计划分三期讲完。下一期将讲解char类型以及类型转换,而第三期推文将讲解更加硬核的浮点类型的底层实现以及浮点数的精度问题,有需要的同学们可以点个关注追番呀。

有任何Java学习相关的问题,可以在公众号后台给小猿留言哦。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值