今天看JavaScript又看到说浮点数的计算会出现精度丢失,学C和Java时的基本语法书上就说浮点数的计算会出现精度丢失,现在JavaScript也这样说,于是我真是忍不住了非要弄明白为什么这样说,为什么会出现精度丢失。因为我之前看书基本语法这些章节基本上都是跳过或粗略看一下的。
然后上网查了一些资料,没想到牵扯到了不少相关的知识,比如原码补码反码等等,于是我又将相关的知识也学习了一下或复习了一下。
原码:原码表示法在数值前面增加了以为符号位,(即最高位为符号位):正数该位为0,负数该位为1(0有两种表示:+0和-0),其余位表示数值的大小。原码当然不能直接参加运算,因为它的符号位也会参与计算,所以可能会出错。所以由于原码的符号位不能直接参与运算,必须和其他位分开,所以如果要用一个数的原码做加减法必然会这就增加了硬件的开销和复杂性。原码当然也分整数和小数:
小数的原码:
在符号位上用"0"表示正数;用"1"表示负数。数值位表示真值的绝对值。凡不足n-1位的,小数在最低位右边加零;整数则在最高位左边加零已补足n-1位。这种计算机的编码形式叫做原码。
补码:
在计算机系统中,数值一律用补码来表示(存储)。 主要原因:使用补码,可以将符号位和其它位统一处理;同时,减法也可按加法来处理。另外,两个用补 码表示的数相加时,如果最高位(符号位)有进位,则进位被舍弃。
正数的补码和原码相同。
负数的补码等于其符号位不变,数值部分的各位取反,然后整个数加1。
小数补码求法:一种简单的方式,符号位保持1不变,数值位从右边数第一个1及其右边的0保持不变,左边按位取反再加1(-0.0101为负数,补码为1.1011。)。
另外:同一个数字在不同的补码表示形式里头,是不同的。比方说-15的补码,在8位2进制里头是11110001,然而在16位2进制补码表示的情况下,就成了1111111111110001。在这篇补码概述里头涉及的补码转换默认把一个数转换成8位2进制的补码形式,每一种补码表示形式都只能表示有限的数字。
补码相当重要的一个用途就是可以把减法换算成加法来做,而这其中又设计到一个称之为“模”的概念:
比如时钟的计量范围是0~11,模=12。又比如8位字长,能表示的最大数是2的8次方减1,则2的8次方就是模。
“模”实质上是计量器产生“溢出”的量,它的值在计量器上表示不出来,计量器上只能表示出模的余数。任何有模的计量器,均可化减法为加法运算
例如:假设当前时针指向10点,而准确时间是6点,调整时间可有以下两种拨法:
一种是倒拨4小时,即:10-4=6
另一种是顺拨8小时:10+8=12+6=6
在以12模的系统中,加8和减4效果是一样的,因此凡是减4运算,都可以用加8来代替。
对“模”而言,8和4互为补数。实际上以12模的系统中,11和1,10和2,9和3,7和5,6和6都有这个特性。共同的特点是两者相加等于模。
So:在这样的系统中减法问题也可以化成加法问题,只需把减数用相应的补数表示就可以---因为减数的补码加上被减数就是“模”。因为补码的引进就是为了解决机器上减法运算不便的,思想是使符号位参与运算,即用补码表示的负数进行加法运算就相当于减去了这个数。而加上一个正数就是加上了一个正数,不需要进行什么改变,它的“补码表示”自然不需要做什么改变啦。
而反码的概念就比较简单了,但可不是“只用把1变0把0变1就行了”!
反码表示法规定:正数的反码与其原码相同;负数的反码是对其原码逐位取反,但符号位除外。
今天晚上只看了这么多,至于为什么浮点数的运算为产生精度丢失再抽时间来学习研究一下。