36

为什么0.1+0.2不等于0.3?原来编程语言是这么算的……

 4 years ago
source link: https://www.tuicool.com/articles/yeaq2ma
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

从小我们就知道 0.1 + 0.2=0.3。但是,在光怪陆离的计算世界中,运算方式却大相径庭。

我最近开始用 JavaScript 进行编码,在阅读数据类型时,我注意到 0.1 + 0.2 不等于 0.3 的奇怪行为。我向 Stack Overflow 寻求帮助,在上面找到了一些有用的帖子。如下图所示:

BJvyyaB.png!web

Stack Overflow 界面图像。

经过大量的研究和数学运算后,我得出结论,这不是错误。这是数学运算中的浮点运算。让我们进一步了解内在机制。

问题描述: 为什么 0.1 + 0.2 = 0.30000000000000004?

如果你用 Java 或 C 语言编过程,那你一定知道用于存储值的不同数据类型。我们在前面的讨论中将考虑两种数据类型:整数型和浮点型。

整数型存储整数,而浮点型存储小数。

在这之前,我们先来了解一个小概念:为了实现计算,数字是如何表示的?极小数和极大数通常用科学计数法表示,即:

yArYR3r.png!web

同样,如果一个用科学计数法书写的数字小数点前有一个非零的十进制数,则该数字是标准化写法。例如,0.0005606 用科学计数法的标准化写法为:

RF3miui.png!web

Significant 是指不包含零的有效数字,base 表示所使用的进制——此处为十进制(10)。Exponent(指数)表示小数点需要向左或向右移动的步数。

现在,有两种显示浮点数的方法:单精度和双精度。在进行浮点运算时,单精度使用 32 位,而双精度使用 64 位。

与许多其他编程语言不同,JavaScript 并未定义不同类型的数字数据类型,而是始终遵循国际 IEEE 754 标准,将数字存储为双精度浮点数。

这种格式以 64 位存储数字,其中数字(分数)存储在位 0 到 51 中,指数存储在位 52 到 62 中,符号存储在位 63 中。

yyuiMbb.png!web

IEEE754 双精度标准。

我们按 IEEE754 标准用 64 位表示 0.1。第一步是将十进制的 0.1 转换为二进制的 0.1。首先将 0.1 乘以 2,然后将小数点前的数字分离出来,得到其相应的二进制数。

eM7jIfa.png!web

重复此操作至 64 位。然后把它们按升序排列,获取尾数,再根据双精度标准,我们将把其四舍五入到 52 位。

UrIzEnz.png!web

尾数

用科学计数法表示二进制 0.1 并只保留前 52 位:

UFNVFrm.png!web

尾数部分处理好后。现在我们用下面的方式处理指数:

JjuUzmR.png!web

这里,11 代表我们要使用的 64 位表示的指数位数,-4 代表科学计数中的指数。

所以最终数字 0.1 的表示形式是:

JvARfui.png!web

同理,0.2 表示为:

7vqYJbM.png!web

将两个数相加,得到:

r2ABvyb.png!web

转换为浮点数,它变成:

f6ba2eB.png!web

这就是 0.1 + 0.2 = 0.30000000000000004 的原因。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK