JAVA语法中关于!和~运算符的辨析
1.首先明白机器数 真值的区别
机器数: 一个数在计算机中的二进制表示形式,叫做这个数的机器数。首位为符号位,后面的位数表示这个数的绝对值 。以byte为例 ,+3在机器中存储为0000 0011; -3在机器中存储为1000 0011。其中0000 0011和1000 0011称为机器数
真值:由于最高位为符号位,故机器数的表示出来的值就不等于真正的数值。
例如上面的有符号数 1000 0011,其最高位1代表负,其真值是 -3,而不是形式值131(1000 0011转换成十进制等于131)。所以,为区别起见,将带符号位的机器数对应的真正数值称为机器数的真值。
例如 0000 1000 = +000 1000 =8 ; 1000 1000 = -000 1000 = -8
2.搞明白原码 反码 补码(正数的原码 反码 补码均是一样的)
原码:就是机器数 如byte型的9在机器中表示为0000 1001 -8表示为1000 1000
反码:负数的反码,相较于原码而言 符号位不变 其余取反 。-8的反码为1111 0111
补码:负数的补码,在反码的基础上加一。-8的补码为1111 1000
tips:引入反码 是为了解决计算机中加减法的问题
引入补码 是为了解决+0与-0的问题
我们规定补码1000 0000 表示-128
下面为相关解释
首先来看原码:
计算十进制的表达式: 1 - 1 = 0
1 - 1 = 1 + (-1) = [0000 0001]原+ [1000 0001]原= [1000 0010]原= -2
如果用原码表示,让符号位也参与计算,显然对于减法来说,结果是不正确的。这也就是为何计算机内部不使用原码表示一个数。
为了解决原码做减法的问题, 出现了反码:
计算十进制的表达式:1 - 1 = 0
1 - 1 = 1 + (-1) = [0000 0001]原+ [1000 0001]原= [0000 0001]反+ [1111 1110]反= [1111 1111]反= [1000 0000]原= -0
发现用反码计算减法,结果的真值部分是正确的。而唯一的问题其实就出现在"0"这个特殊的数值上,虽然人们理解上+0和-0是一样的,但是0带符号是没有任何意义的,而且会有[0000 0000]原和[1000 0000]原两个编码表示0。
于是补码的出现,解决了0的符号问题以及两个0的编码问题:
1-1 = 1 + (-1) = [0000 0001]原+ [1000 0001]原= [0000 0001]补+ [1111 1111]补= [1 0000 0000]补=[0000 0000]补=[0000 0000]原注意:进位1不在计算机字长里。
这样0用[0000 0000]表示,而以前出现问题的-0则不存在了。而且可以用[1000 0000]表示-128
-128的由来如下:
(-1) + (-127) = [1000 0001]原+ [1111 1111]原= [1111 1111]补+ [1000 0001]补= [1000 0000]补
-1-127的结果应该是-128,在用补码运算的结果中,[1000 0000]补就是-128,但是注意因为实际上是使用以前的-0的补码来表示-128,所以-128并没有原码和反码表示。(对-128的补码表示[1000 0000]补,算出来的原码是[0000 0000]原,这是不正确的)
计算机用补码存储数据使用补码,不仅仅修复了0的符号以及存在两个编码的问题,而且还能够多表示一个最低数。这就是为什么8位二进制,使用原码或反码表示的范围为[-127, +127],而使用补码表示的范围为[-128, 127]。
3.关于~与!的解释
1.~ 对计算机中存储的补码进行取反(每一位都要进行取反)
-3的原码 1000 0011
-3的反码 1111 1100
-3的补码 1111 1101
~(-3)为0000 0010(这是一个数字的补码,且这个数字是正数)故推出这个数字为2
8的原码 反码 补码都是 0000 1000
~8后为1111 0111(这是一个负数的补码) 故推出这个负数为-9 (负数的补码的补码等于原码)
2.“!”是非运算符号,比如a为真,则!a则为假。a如果为假,则!a为真。