八种基本数据类型一览表
备注:1丶(short)0表示的是默认值是short类型的0,(byte)0同理。
2丶0L表示长整型的0,0.0f、0.0d同理,其中字母L、f、d不区分大小写只是作为数据类型的标识用于区分数据的类型。
3丶"\u0000"是Unicode码,代表一个空格的意思。
4丶除了boolean类型,其它数据的表示范围,不必强记(言下之意就是除此之外的都要求记忆)。
分类图:
我们先来看这样一道Java程序题:
细心的童鞋可能已经发现了上表中“可表示的数据范围”有着某种规律:-x~x-1。
在计算机内,定点数有3种表示法:原码、反码和补码
原码就是二进制定点表示法,即最高位为符号位,“0”表示正,“1”表示负,其余位表示数值的大小。如(1000 0000)就表示-0。
反码表示法规定:正数的反码与其原码相同;负数的反码是对其原码逐位取反,但符号位除外。如(1000 0000)的反码就是(1111 1111)(一样表示为-0)。
补码表示法规定:正数的补码与其原码相同;负数的补码是在其反码的末位加1。
那么用byte类型举例,一个字节的它最大的二进制数就是(0111 1111),首先0是符号位,而且正数的源码和补码相同,所以换算成十进制就是127。在这里又要明确一点:补码和反码都是原码经过变换得来的,我们最终都要把补码或者反码转换成原码进行计算。也就是说,问题是:一个怎么样的八位二进制数补码换算成原码时是最小的。
我们先来进行这样一个运算:
原码八位二进制数范围:(1111 1111)~(0111 1111);换算成十进制是:-127~127。
由补码和原码的关系可以得补码八位二进制数范围:(1000 0001)~(0000 0001);换算成十进制:-127~127。
这时读者肯定会吐槽我,这有什么鬼用?
先等等,你是否有一个疑惑?
我们由原码通过运算得出的补码八位二进制数范围:(1000 0001)~(0000 0001),这个范围是真的吗(逻辑上并没错)?为什么不是(1000 0000)~(0000 0000)呢?我相信你决不可能一眼看出(1000 0000)与(1000 0001)这两个补码的大小,它们谁大?
那我们来实践一下,(0000 0000)是整数(原码变补码第一位符号位是不会变的,所以可以通过补码直接看出是正数)那它表示的就是0。等等,问题来了,(1000 0000)换算成原码应该先减一然后除符号位取反,但是(1000 0000)怎么减一(1是符号位,不参与运算)?难道(1000 0000)补码代表的就是那个数?
要解决以上问题,还得从头说起:
数值有正负之分,计算机就用一个数的最高位存放符号(0为正,1为负)。这就是原码的由来。我们假设机器能处理的位数为8。即字长为1byte,那么原码能表示数值的范围为(-127~-0 +0~127)共256个。
有了数值的表示方法就可以对数进行算术运算。
但是很快就发现用带符号位的原码进行乘除运算时结果正确,而在减法运算的时候就出现了问题(假设字长为8bits):
( 1 ) 10 - ( 1 )10 = ( 1 )10 + ( -1 )10 = ( 0 )10
(0000 0001)原 + (1000 0001)原 = (1000 0010)原 = ( -2 ) 错误
因为在两个整数的加法运算中是没有问题的,于是就发现问题出现在带符号位的负数身上。
所以对除符号位外的其余各位逐位取反就产生了反码。反码的取值空间和原码相同且一一对应。
下面是反码的减法运算:
( 1 )10 - ( 1 ) 10= ( 1 ) 10 + ( -1 ) 10= ( 0 )10
(0000 0001) 反+ (1111 1110)反 = (1111 1111)反 = ( -0 ) 错误
( 1 )10 - ( 2)10 = ( 1 )10 + ( -2 )10 = ( -1 )10
(0000 0001) 反+ (1111 1101)反 = (1111 1110)反 = ( -1 ) 正确
问题出现在(+0)和(-0)上,在人们的计算概念中零是没有正负之分的。
为了解决这个问题,人们引入了补码概念。补码表示的数据数量和原码一样但数值不完全相同。
在八位二进制(别的位数可以“正常”表示-128)补码中用(-128)代替了(-0)。
所以八位二进制数补码的表示范围为:(-128~0~127)共256个。
同时我们规定(-128)的八位二进制补码是(1000 0000)。
值得一提的是(-128)在八位二进制数里面没有原码和反码(在其它位数有多个)——如果你还问为什么那就说明你没认真看上文。
抛砖引个玉:其实(-128)只是八位二进制数里的特例,在其它位数中取代(-0)成为特例的数还有很多,但它们都有一个共同特点:它们只是个别位数下的奇葩,在其它位数里它们和别的数并无差别。
总结几点:
1丶计算机由原码衍生出反码、补码其目的都是解决原码所存在的问题,这一点和数学的发展史其实是类似的:都是为了解决老知识(规则)所不能解决的问题而创造了新知识(规则)(比如0和负数的引入,都是为了解决运算问题)。
2丶补码设计目的:通俗的说就是为了解决原码运算会出错的问题而制定一套编码,因为原码所表示的数中是有(-0)这个数的,为了阻止它的扰乱,人类才创造出了补码(其实还涉及到一个“模”的概念)。科学地说:补码能使符号位与有效值部分一起参加运算,从而简化运算规则;使减法运算转换为加法运算,进一步简化计算机中运算器的线路设计。
3丶由于在Java中采用的是补码(原因2丶),而补码表示的个数和原码是一样的,但是范围不一样。这也是为什么我们无法用计算得出(-128)这个数,因为这个是规则(不了解它不可能知道(-128)的由来),就好像数学中规定任何数的0次幂都是1。这是人类为了解决问题而制定的规则。