Java教程

java精度损失

本文主要是介绍java精度损失,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
byte b = (byte)322;
System.out.println(b);//66

传统计算机底层只能识别二进制,存储和读取数据时都要使用二进制数表示。

强制把int类型转成byte类型,int类型是4个字节32位,用这32位表示322,即00000000 00000000 00000001 01000010,现在要转成只能装下一个字节的byte,只能把前面24位截掉成01000010

表示的数即66;

以下只以一个字节为例,多了就截掉

表示正负数

采用最高位是符号位的方法来区分正负数,正数的符号位为0、负数的符号位为1。

byte b = (byte)130;//b = -126

00000000 00000000 00000000 10000010符号位是0,表示正的130;把前面截掉成10000010,最高位是符号位1,即负数,所以是-2?(二进制的10代表十进制的2)

其实计算机存储数据时,一律储存二进制的补码形式

对于正整数来说,原码、反码、补码相同

对于负数来说
byte b = -1;
二进制原码:10000001
反码:符号位不变,其它位取反,11111110
补码:反码+1,11111111

可以发现,一个负数的反码+这个负数的相反数(即正数)的原码=11111111
负数的反码可以用11111111-正数的原码表示
如-1的反码=1111111-00000001=11111110

补码再转回原码?要反推吗?
补码:11111111
反码:补码-1,11111110
原码:10000001

不用反推,把补码当成原码,正推即可得到10000001
原码:11111111
反码:10000000
补码:10000001

10000010其实是补码,-> 11111101 -> 111111101111110转为10进制为126,加上符号1,表示-126

可能有人不知道1111111-00000001=11111110

补充一下减法规则:0-0=0,10-1=1(向高位借位) 1-0=1,1-1=0

2-1=1
  00000100
- 00000011
= 00000001

37-27=10
  00100101
- 00011011
= 00001010

为什么计算机要以补码形式存储

计算机只会算二进制的加法,不会减法,0+1=1,0+0=0,1+1=10(进位,别问为什么不是2了)

计算机是怎么进行加法运算的

以1个字节为例

2+3=5
 00000010
+00000011
=00000101,即5

42+99=141
 00101010
+01100011 
=10001101,即141

14+7=21
 00001110
+00000111
=00010101
说明一下,十位1+1=10,把0写下边,1进位
百位1+1还要加进位的1,即11,把1写下边,1进位
千位1+0加进位的1,即10,把0写下边,1进位

那么1-1怎么算,只能转成1+(-1)

 00000001
+10000001
=10000010,-2

1-1=-2看来行不通,使用原码根本不适合减法运算

使用反码做减法运算

还是1-1,正数的反码不变,负数的反码是除了符号位不变,其它位取反10000001->11111110

两个数转换为反码,后相加得到的反码再转成原码

1+(-1)=0
00000001
+11111110
=11111111

11111111->10000000即-0即0

2+(-1)=1
  00000010
+ 11111110 
=100000000,即00000000,0错了

解决了1-1=0的问题

但是以反码存储,+0是00000000,-0是11111111,同样的一个0,以反码储存却有两种形式

使用补码做减法运算

1的补码是00000001,-1的补码是11111111

1+(-1)=0
  00000001
+ 11111111
=100000000,最高存储8位,舍去1,剩下的8个0是非负数,补码与原码一致,为0

2+(-1)=1
  00000010
+ 11111111
=100000001,舍去最高位1,即00000001,1

54+(-53)=1
-53的原码10110101->11001010->11001011
  00110110
+ 11001011
=100000001,...1

24+(-4)=20
-4的原码10000100->11111011->11111100
  00011000
+ 11111100
=100010100,截掉1得00010100,即20

-127+(-1)=-128
  10000001
+ 11111111
=110000000->10000000(谁的补码是10000000,好像目前还没有(别说是0))

0的补码是00000000,-0的补码(原码10000000->反码11111111->补码100000000),超出8位,截去1,剩下00000000,存储时都是00000000解决了存储0时不一致的问题

使用补码存储的目的是为了解决减法运算问题

怎么用一个字节表示-128

byte使用1个字节表示了-128~127范围的整数,-127和127还好表示

一律存储补码形式,非负数的补码、反码、原码都一致

0到127

0   : 00000000
1   : 00000001
2   : 00000010
....
126 : 01111110
127 : 01111111

128 : 10000000错 最高位是符号位别占用

-0到-127

-0  : 10000000 -> 11111111 -> 100000000 -> 00000000
-1  : 10000001 -> 11111110 -> 11111111
-2  : 10000010 -> 11111101 -> 11111110
-3  : 10000011 -> 11111100 -> 11111101
-4  : 10000100 -> 11111011 -> 11111100
-5  : 10000101 -> 11111010 -> 11111011
-6  : 10000110 -> 11111001 -> 11111010
-7  : 10000111 -> 11111000 -> 11111001
...
-126: 11111110 -> 10000001 -> 10000010
-127: 11111111 -> 10000000 -> 10000001

如果以原码的形式存储数据,1个字节最多表示-127到127的整数,128是9位存储不了

-128 : 110000000 -> 101111111 -> 110000000 -> 10000000

但是使用补码的形式存储,发现还有10000000没用上,使用10000000表示-128没有问题

byte num1 = -127;
byte num2 = -2;
System.out.println((byte)(num1+num2));//127

-127+(-2)为什么是127
  10000001
+ 11111110
=101111111->强转成byte只有8位,截去后的结果01111111->127
    
-127+(-7)为什么是122
  10000001
+ 11111001
=101111010->01111010->122
   
上面得到的结果是正数,所以补码与原码相同,不需要取反+1    
-3+(-4)=-7
   11111101
+  11111100
= 111111001->100000110->100000111->-7

可以看到负数转为补码做减法运算很方便,只是超出了[-128~127]范围后会有精度损失

int num1 = 2147483647;
int num2 = 7;

System.out.println(num1+num2);//-2147483642

2147483647的补码原码相同如下
  01111111 11111111 11111111 11111111
+ 00000000 00000000 00000000 00000111
= 10000000 00000000 00000000 00000110  结果是负数的补码与原码不一致
      
10000000 00000000 00000000 00000110
11111111 11111111 11111111 11111001
11111111 11111111 11111111 11111010//最前面的是符号位,即-2147483642

使用工具验证时记得把空格去掉

补码为什么等于反码+1

两个相反数相加要等于0,如1+(-1)=0,以原码储存负数做不到,需要换个编码形式存储负数,暂且设这个码已经存在,为*码

  11111111
+ 00000001
=100000000->00000000->即0

设一个正数,则它的相反数暂时称负数,正数+(负数)=0,同时负数以*码存储

正数的原码+(负数的*码)=00000000=11111111+00000001
(11111111-正数的原码)+00000001=(负数的*码)

负数的反码+00000001=负数的*码
负数的*码 = 负数的反码+1

现在称*码为补码,补码=反码+1

头铁的可以算补码 = 0 - 正数的原码
我还在网上找了个比较简单的补码算减法 补码是减法

这篇关于java精度损失的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!