@XingdingCAO
2017-06-16T22:20:38.000000Z
字数 3843
阅读 5521
浮点数
整数
参考wiki百科条目:
有符号数处理 https://zh.wikipedia.org/wiki/%E6%9C%89%E7%AC%A6%E8%99%9F%E6%95%B8%E8%99%95%E7%90%86
算术溢出 https://zh.wikipedia.org/wiki/%E7%AE%97%E8%A1%93%E6%BA%A2%E5%87%BA
分类:有符号整数、无符号整数
表示
我们都知道,计算机数据都是以二进制比特位存储的,所以要将十进制数转化为二进制码。
先介绍算机中几个二进制码的概念:
有符号整数
0
:正数 1
:负数),剩余位为数值位(就是将2进制数转化为10进制数)。1
,也就是数值位加上一,进位时不影响符号位。但是只针对负数,正数保持原码不变。无符号整数(自然数|非负数)
在大多数计算机中存储的就是补码,整数以补码表示。
在有符号整数中0怎么表示?
因为0既不属于正数也不属于负数,但按照原码表示,0包括+0和-0两种表示方式,导致0的补码不唯一。而实际上,0的补码是唯一的,是+0的补码。那么-0的补码表示什么呢?为什么+0的补码代表0呢?在搞懂下面的数学原理,我们才能真正理解补码的运用,而不是从表面上去换算,然后将不符合统一规则的数单独去作规定。
背后的数学原理
知乎上的一个回答阐释了补码表示的数学原理:
运算
取余:将被除数赋值给变量,当变量减去除数的结果大于除数时,结果赋值给变量,继续循环直到结果小于除数,这个值就是余数。
有限位数的补码能表示的范围是有限的,又因为补码的循环特性,所以有可能发生计算结果错误的情况。这时,我们称之为溢出。
定义: 运行单项数值计算时,当计算产生出来的结果是非常大的,大于寄存器或内存所能存储或表示的能力限制。
另一个定义: 当长度为n位的两个二进制数经过加减法器运算,得到的长度为n位的结果不是正确值时,我们说发生溢出。
接下来举几个正常运算和溢出的例子(16位整数)
原码 | 反码 | 补码 |
---|---|---|
4 | 0000000000000100 | 0000000000000100 |
3 | 0000000000000011 | 0000000000000011 |
-4 | 1000000000000100 | 1111111111111011 |
32767(2^15-1) | 0111111111111111 | 0111111111111111 |
-32768(2^15) | 1000000000000000 | 1111111111111111 |
3 + 4 = 0000000000000011B + 0000000000000011B = 0000000000000111B = 7
//正常运算
3 + (-4) = 0000000000000011B + 1111111111111100B = 1111111111111111B = -1
//正常运算
4 + (-4) = 0000000000000100B + 1111111111111100B = ~~1~~ 0000000000000000B = 0
//正常运算
//但是注意运算结果丢失了一位,这并不是溢出,而是**最高位进位**。**进位**并不一定是**溢出**,因为当进位的结果和我们预期的是一样的时**不溢出**,而只有产生不正确的值时才是**溢出**。在加法器的电路设计中,**进位**也是重要的一部分。只处理二个位元相加,无法考虑进位的称为**半加法器**,能处理二个位元及一个进位位元相加的才称为**全加法器**。
32767 + 3 = 0111111111111111B + 0000000000000011B = 1000000000000010B = -32766
//溢出
-32768 + (-4) = 1000000000000000B + 1111111111111100B = ~~1~~ 0111111111111100B = 32764
//同时发生溢出和进位
32767 * 3 = 0111111111111111B + 0111111111111111B + 0111111111111111B = ~~1~~ 0111111111111101B = 32765
//同时发生溢出和进位
总结:溢出发生在越过(方向不限)正数与负数的边界时。进位则是发生在向右越过最右端的边界、向左越过正数与负数的边界时。
参考wiki条目
标准
IEEE二进制浮点数算术标准(IEEE 754)是20世纪80年代以来最广泛使用的浮点数运算标准,为许多CPU与浮点运算器所采用。这个标准定义了表示浮点数的格式(包括负零-0)与反常值(denormal number)),一些特殊数值(无穷(Inf)与非数值(NaN)),以及这些数值的“浮点数运算符”;它也指明了四种数值舍入规则和五种例外状况(包括例外发生的时机与处理方式)。
表示
浮点数在计算机中的表示是以二进制的科学记数法(eg -1.1100101E-13)表示的,所以比特位分配三个部分:符号位、指数部分和分数部分。
符号(sign) | 指数(exponent) | 分数(fraction) |
---|---|---|
float(32-bit) | 1 | 8 |
double(64-bit) | 1 | 11 |
符号位:0-正数 1-负数
指数:二进制科学记数法中的指数部分
分数:二进制科学记数法中的尾数的小数部分
指数偏移值
指数偏移值(exponent bias),是指浮点数表示法中的指数域的编码值为指数的实际值加上某个固定的值,IEEE 754标准规定该固定值为,其中的为存储指数的比特的长度。
以单精度浮点数为例,它的指数域是8个比特,固定偏移值是。单精度浮点数的指数部分实际取值是从-127到128,加上127的偏移量之后变为0到255,为八位无符号整数的范围。例如指数实际值为,在单精度浮点数中的指数域编码值为,即。
采用指数的实际值加上固定的偏移值的办法表示浮点数的指数,好处是可以用长度为个比特的无符号整数来表示所有的指数取值,这使得两个浮点数的指数大小的比较更为容易,实际上可以按照字典序比较两个浮点表示的大小。
这种移码表示的指数部分,中文称作阶码。
规约形式的浮点数
如果浮点数中指数部分的编码值在之间,且尾数部分最高有效位(即整数字)是1,那么这个浮点数将被称为规约形式的浮点数。“规约”是指用唯一确定的浮点形式去表示一个值。
IEEE754采用规约浮点数,而IEEE754-1985标准采用非规约浮点数 。原理略复杂,有兴趣自行了解。
特殊值
精度
在二进制,第一个有效数字必定是“1”,因此这个“1”并不会存储。
单精和双精浮点数的有效数字分别是有存储的23和52个位,加上最左手边没有存储的第1个位,即是24和53个位。
由以上的计算,单精和双精浮点数可以保证7位和15位十进制有效数字。
例如:100.1*2==200.2(这个好理解就不测试了) 100.1*3!=300.3(为什么不等于?)
System.out.println(100.1*3);
System.out.println(Long.toBinaryString(Double.doubleToLongBits(100.1)));
System.out.println(Long.toBinaryString(Double.doubleToLongBits(200.2)));
System.out.println(Long.toBinaryString(Double.doubleToLongBits(100.1*2)));
System.out.println(Long.toBinaryString(Double.doubleToLongBits(100.1*3)));
System.out.println(Long.toBinaryString(Double.doubleToLongBits(300.3)));
double类型的浮点数的有效位是15-16位,可以看出因为100.1本身就能不精确地被表示,离实际值差那么一点儿,而在进行乘法之后这点偏离被放大了,乘以二的时候舍弃更低位的数据,还能正确表示;但乘以三的时候就已经在有效数字的第十七位丢掉了一半的值,导致前十六位的有效位没有进位,从而出现了100.1*3!=300.3的情况。