以下是引用pangding在2011-3-25 22:36:02的发言:
说了今天晚上来答,不能食言~
首先你得知道怎么把十进制转成二进制。
手算时要分别转整数和小数。
整数是不断除二,反向取余数:
比如 13 = 8 + 4 + 1,即 2^3 + 2^2 + 2^0。所以二进制是 1101
可用竖式算得,如下:
1 | 13 (13 除以2 商6 余1)
0 | 6
1 | 3
1 | 2
| 0
算法是,不断除二,把商写在下面,余数写在左边。(这个不太好打,你就凑合着看把,反正会算就行)
然后从下往上看就是 1101
小数部分是不断乘二,取整数部分。
比如 0.8125 = 0.5 + 0.25 + 0.0625 = 1/2 + 1/4 + 1/16,所以写成二进制是 0.1101
用竖式算是:
1 | 0.8125 (0.8125 乘以2得1.625,整数部分是1,小数部分是0.625)
1 | 0.625
0 | 0.25
1 | 0.5
| 0
算法是,不断乘二,把整数部分写在左边,小数部分写在下面。到0为止。
这个是从上往下看的。所以就是 0.1101
此时就有 13.8125 写成二进制就是 1101.1101。
先会这个,再看你那个题。
咱们先算 3.3
3 = 2+1,即是二进制 11,没得说。
0.3:
0 | 0.3
1 | 0.6
0 | 0.2
0 | 0.4
1 | 0.8
1 | 0.6
... ...
0.6 又出现了。
这就比较有意思了,虽然 0.3 在十进制里很平常,但在二进制里是无限循环小数。
所以 3.3 是 11.0100110011001....
同理可以算得 1.1 是 1.0001100110011001....
这两个都是无限小数,在计算机这种二进制的世界里显然无法精确表示。
IEEE 标准里(好像是754标准吧,是个规定浮点运算相关的标准)规定了浮点数的表示方法。
统一用类似科学计数法的方法表示:m * 2^n。
m 称为尾数(Fraction),n 称为阶数或者指数(exponent),还有一个符号位来表示正负。
并要求要调整指数,比如 1101.1101 * 2^0 要写成 1.1011101 * 2^3。此时指数就是 3,当然要用二进制的11;尾数是 1.1011101。由于这么一移,整数部分必然是用1开头的,没必要多浪费一位。所以尾数部分不储存首1。即 m 是 1011101。
当然情况没这么简单,还可能有的数是 0.001,就得写成 1 * 2^-3。这么一来,指数又得有正负,为了避免这个,指数还要用偏移码的方法表示。
标准里规定的比较详细。我这说的叫正规浮点数,还有非正规的。还有用来表示正负无穷大的(INF),表示无意义值的(NaN),无意义值又有很多种类,用于表示不同的错误,等等。我不详细介绍了。
具体来说:
32位(4字节的,相当于 float)的浮点数,有1位符号位,8位指数位,23位尾数位。
64位(8字节的,如double),有1位符号,11位指数,52位尾数。
好像标准里还有16位的,我记不得了。有兴趣的自己查。
既然尾数有限,就存不下无限小数,存不下的值就抛弃了。
至于你问为什么除出来是 2.99999 这个其实可以算,但我实在不想算了。
乘除法的机器算法用的是位移加减,你可以自己手算。查下资料,把那52位数写出来,先指数对齐,再位移最多不超过52次,做减法,就能得到。
我觉得楼主大概知道这么多,也不是那么疑惑了。这些知识在计算机组成原理里都有讲。
会不会这些,对 C 这种高级语言来说不是那么太重要了。同样的算法在不同的编译器上,不同的系统上,不同的硬件上,跑下来的值在小数点很后面的几位有点微小的偏差是很正常的事情。(虽然 GNU 的有些文档,也指出这种行为是它们软件的BUG,不过他们说了这种 BUG 改善不了)
不要太纠结。一般来说把低精度的往高数度的转,如 int 转 double 是没问题的,相反的转法一般是不可取,精度损失太大。
像这个例子,根据 2楼 提供的数据,本来 double 算下来,誤差也就是几百亿分之一,结果一转变成1了。好的编译器,应该在这种情况给中等级别以上的警告提示。
硬件方面我也只是会 intel 的 80x86 的架构。别的不是很清楚,不过这个工程标准规定的东西,肯定什么设备都一样。
太牛了!今天值了!
说了今天晚上来答,不能食言~
首先你得知道怎么把十进制转成二进制。
手算时要分别转整数和小数。
整数是不断除二,反向取余数:
比如 13 = 8 + 4 + 1,即 2^3 + 2^2 + 2^0。所以二进制是 1101
可用竖式算得,如下:
1 | 13 (13 除以2 商6 余1)
0 | 6
1 | 3
1 | 2
| 0
算法是,不断除二,把商写在下面,余数写在左边。(这个不太好打,你就凑合着看把,反正会算就行)
然后从下往上看就是 1101
小数部分是不断乘二,取整数部分。
比如 0.8125 = 0.5 + 0.25 + 0.0625 = 1/2 + 1/4 + 1/16,所以写成二进制是 0.1101
用竖式算是:
1 | 0.8125 (0.8125 乘以2得1.625,整数部分是1,小数部分是0.625)
1 | 0.625
0 | 0.25
1 | 0.5
| 0
算法是,不断乘二,把整数部分写在左边,小数部分写在下面。到0为止。
这个是从上往下看的。所以就是 0.1101
此时就有 13.8125 写成二进制就是 1101.1101。
先会这个,再看你那个题。
咱们先算 3.3
3 = 2+1,即是二进制 11,没得说。
0.3:
0 | 0.3
1 | 0.6
0 | 0.2
0 | 0.4
1 | 0.8
1 | 0.6
... ...
0.6 又出现了。
这就比较有意思了,虽然 0.3 在十进制里很平常,但在二进制里是无限循环小数。
所以 3.3 是 11.0100110011001....
同理可以算得 1.1 是 1.0001100110011001....
这两个都是无限小数,在计算机这种二进制的世界里显然无法精确表示。
IEEE 标准里(好像是754标准吧,是个规定浮点运算相关的标准)规定了浮点数的表示方法。
统一用类似科学计数法的方法表示:m * 2^n。
m 称为尾数(Fraction),n 称为阶数或者指数(exponent),还有一个符号位来表示正负。
并要求要调整指数,比如 1101.1101 * 2^0 要写成 1.1011101 * 2^3。此时指数就是 3,当然要用二进制的11;尾数是 1.1011101。由于这么一移,整数部分必然是用1开头的,没必要多浪费一位。所以尾数部分不储存首1。即 m 是 1011101。
当然情况没这么简单,还可能有的数是 0.001,就得写成 1 * 2^-3。这么一来,指数又得有正负,为了避免这个,指数还要用偏移码的方法表示。
标准里规定的比较详细。我这说的叫正规浮点数,还有非正规的。还有用来表示正负无穷大的(INF),表示无意义值的(NaN),无意义值又有很多种类,用于表示不同的错误,等等。我不详细介绍了。
具体来说:
32位(4字节的,相当于 float)的浮点数,有1位符号位,8位指数位,23位尾数位。
64位(8字节的,如double),有1位符号,11位指数,52位尾数。
好像标准里还有16位的,我记不得了。有兴趣的自己查。
既然尾数有限,就存不下无限小数,存不下的值就抛弃了。
至于你问为什么除出来是 2.99999 这个其实可以算,但我实在不想算了。
乘除法的机器算法用的是位移加减,你可以自己手算。查下资料,把那52位数写出来,先指数对齐,再位移最多不超过52次,做减法,就能得到。
我觉得楼主大概知道这么多,也不是那么疑惑了。这些知识在计算机组成原理里都有讲。
会不会这些,对 C 这种高级语言来说不是那么太重要了。同样的算法在不同的编译器上,不同的系统上,不同的硬件上,跑下来的值在小数点很后面的几位有点微小的偏差是很正常的事情。(虽然 GNU 的有些文档,也指出这种行为是它们软件的BUG,不过他们说了这种 BUG 改善不了)
不要太纠结。一般来说把低精度的往高数度的转,如 int 转 double 是没问题的,相反的转法一般是不可取,精度损失太大。
像这个例子,根据 2楼 提供的数据,本来 double 算下来,誤差也就是几百亿分之一,结果一转变成1了。好的编译器,应该在这种情况给中等级别以上的警告提示。
硬件方面我也只是会 intel 的 80x86 的架构。别的不是很清楚,不过这个工程标准规定的东西,肯定什么设备都一样。