0.30000000000000004

August 14, 2016

In [1]: 0.1+0.2
Out[1]: 0.30000000000000004

首先要说的是,这并不是编程语言的bug,而是十进制转换二进制后无限循环小数的精度问题,其他编程语言也有这种情况,有人专门建了一个网站来收录:http://0.30000000000000004.com/

把小数装入计算机,总共分几步?

<ol>
  <li>
    转换成二进制
  </li>
  <li>
    用科学计数法表示
  </li>
  <li>
    表示成IEEE 754格式
  </li>
</ol>

浮点数的二进制表示

<p>
  十进制小数转化为二进制小数采用<strong>“乘2取整,顺序排列”</strong>的方法。
</p>

<p>
  0.1 的表示是什么?
</p>

<blockquote>
  <p>
    0.1 * 2 = 0.2 整数部分取 0<br /> 0.2 * 2 = 0.4 整数部分取 0<br /> 0.4 * 2 = 0.8 整数部分取 0<br /> 0.8 * 2 = 1.6 整数部分取 1<br /> 0.6 * 2 = 1.2 整数部分取 1<br /> 0.2 * 2 = 0.4 整数部分取 0<br /> …
  </p>
</blockquote>

<p>
  所以0.1 的二进制表示是 0.0001100110011……(无限循环小数)。
</p>

<p>
  如果你仔细观察下会发现:
</p>

<p>
  <strong>0.1 到 0.9 的 9 个小数中,只有 0.5 可以用二进制精确的表示。</strong>
</p>

<p>
  这就引出了一个问题,计算机是无法精确存储一些小数的。
</p>

浮点数的二进制存储

<p>
  <a href="https://en.wikipedia.org/wiki/IEEE_floating_point">IEEE 754</a>将双精度浮点数的64 bit规定为 3 部分:
</p>

<ul>
  <li>
    第一位符号位
  </li>
  <li>
    11位存储指数部分
  </li>
  <li>
    剩下52位存储尾数部分
  </li>
</ul>

<p>
  <img class="alignnone" src="https://i0.wp.com/ww2.sinaimg.cn/large/9cd77f2ejw1f6vabbwldbj20go03dgly.jpg?resize=600%2C121" data-recalc-dims="1" />
</p>

<p>
  &nbsp;
</p>

<p>
  因此我们用IEEE 754规范来计算一下 0.1 + 0.2 吧!
</p>

十进制0.1

<p>
  => 二进制0.00011001100110011…(循环0011)<br /> => 尾数为1.1001100110011001100…1100(共52位,除了小数点左边的1),指数为-4(二进制移码为00000000010),符号位为0<br /> => 最终存储为:0(符号位) 00000000100(11位指数) 10011001100110011…11001(52位尾数)
</p>

十进制0.2

<p>
  => 二进制0.0011001100110011…(循环0011)<br /> => 尾数为1.1001100110011001100…1100(共52位,除了小数点左边的1),指数为-3(二进制移码为00000000011),符号位为0<br /> => 最终存储为:0(符号位) 00000000011(11位指数) 10011001100110011…11001(52位尾数)
</p>

<p>
  两者相加:
</p>

<p>
  0.00011001100110011001100110011001100110011001100110011001 +
</p>

<p>
  0.00110011001100110011001100110011001100110011001100110011 =
</p>

<p>
  0.01001100110011001100110011001100110011001100110011001100
</p>

<p>
  转换成10进制之后得到:0.30000000000000004。
</p>

BTW

<p>
  如果开发一套货币系统,那货币的金额一定要是 “ 1 分钱” 的整数倍,而不是有 “ 0.01 元” 这样的数据,所有的数据都要用整型表示,显示的时候再加上小数点,否则便会出现麻烦。
</p>

Profile picture

Written by Armin Li , a venture capitalist. [Mail] [RSS]