[关闭]
@wxf 2017-04-30T03:50:42.000000Z 字数 1521 阅读 1318

5.小数计算为什么会出错

老马说编程


前面的章节中我们在进行小数运算时,遇到了计算结果不精确的问题。为什么看上去非常简单的运算,计算机怎么会出错了呢?

为什么会出错

实际上,不是运算本身出的错,而是因为计算机根本就不能精确的表示很多树,比如0.1这个数。
计算机是用一种二进制格式存储小数的,这个二进制格式不能精确表示0.1,它只能表示一个非常接近0.1但又不等于0.1的一个数。
在计算机中只能用若干个之和来表达十进制的小数,如下:

二进制 十进制 二进制 十进制
…… …… 0.5
8 0.25
4 0.125
2 0.125
1 …… ……

结合上面的描述,我们来试一试如何表达十进制的0.2,如下:

若干个之和 结果 描述
0.25 太大
0.125 又太小
+ 0.1875 逼近0.2了
++ 0.21875 又大了
++ 0.203125 还是大
++ 0.1953125 接近了
+++ 0.19921875 很接近

由此可见,数字都不能精确表示,在不精确数字上的运算结果不精确也就不足为奇了。

为什么一定要用二进制

为什么不能使用我们熟悉的十进制呢?在最底层,计算机使用的电子元器件只能表示两个状态,通常是低压和高压,对应0和1,使用二进制容易基于这些电子器件构建硬件设备和进行运算。而且效率高。

为什么有的小数计算时精确的

在编程过程中,你会发现有的计算结果是精确的。比如,我用Java写:

  1. System.out.print(0.1f+0.1f);
  2. System.out.print(0.1f*0.1f);

第一行输出0.2,第二行输出0.010000001。按照上面的说法,第一行结果也不对啊?
其实,这是Java语言给我们造成的假象,计算结果其实也是不精确的,但是由于结果和0.2足够接近,在输出的时候,Java选择了输出0.2这个看上去非常精确的数字,而不是一个中间有很多0的小数。

怎么处理计算不精确的问题

计算不精确怎么办?大部分情况下,我们不需要那么高的精度,可以四舍五入,或者在输出的时候只保留固定个数的小数位。
如果真的需要比较高的精度,可以采用如下几种方法:

查看浮点数的二进制形式

我们之前一直在用“小数”这个词表示float和double类型,其实,这是不严谨的,“小数”是在数学中的词,在计算机中,我们一般说的是“浮点数”。float和double被称为浮点数据类型,小数运算被称为浮点运算。
为什么叫浮点数呢?这是由于小数的二进制表示中,表示那个小数点的时候,点不是固定的,而是浮动的。
查看浮点数的二进制形式,在Java中,可以使用如下代码:

  1. Integer.toBinaryString(Float.floatToIntBits(value));
  2. Long.toBinaryString(Double.doubleToLongBits(value));

浮点数运算为什么会出错呢?原因就是:很多小数在计算机中不能精确表示。
计算机的基本思维是二进制的,所以,意料之外,情理之中。
上节我们说了整数的二进制,本节谈了小数。那么字符和文本呢?编码有事怎么回事?乱码是因为什么?


参考资料:
老马说编程(微信号:laoma_shuo)——小数计算为什么会出错?
码农翻身(微信号:coderising)——浮点数为什么不精确?

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注