Giter Site home page Giter Site logo

blog's Introduction

Ask Me Anything !

blog's People

Contributors

tianzhich avatar

Stargazers

 avatar  avatar  avatar

Watchers

 avatar

blog's Issues

markdown table rendering test


title: "Why 0.1+0.2 !== 0.3 in JavaScript?"
date: "2018-08-17"
category: "fed"

写在前面

之前面试遇到过一个问题:为什么JavaScript里面0.1+0.2 !== 0.3。当时我就回答到了浮点数精度有误差,显然面试官不满意,这不谁都知道吗。

小红书上也强调过不要进行0.1+0.2 === 0.3的判断,仅仅提到如下理由:

关于浮点数值计算会产生舍入误差的问题,有一点需要明确:这是使用基于IEEE754数值的浮点计算的通病,ECMAScript并非独此一家;其他使用相同数值格式的语言也存在这个问题。

因此,也仅仅能答出这是因为ECMAScript使用了基于IEEE754标准的浮点数存储导致,但是深入计算机层面,又是为什么?

计算机角度

浮点数

计算机中数的表示有定点数和浮点数

  • 定点数小数点位置确定。小数点在符号位后面成为小数定点机(这类机器只能表示小数),小数点在末尾成为整数定点机(这类机器只能表示整数)。
  • 浮点数表示类似于十进制的科学计数法表示。由阶数(阶码,阶符),尾数(尾码,尾符)构成。尾数的位数决定了浮点数的精度,阶数的位数决定了浮点数的范围 。

浮点数的规格化

  1. 对于小数来说,转换为二进制是乘2取整操作

    运算过程 取整 小数部分
    0.1 * 2 = 0.2 0 0.2
    0.2 * 2 = 0.4 0 0.4
    0.4 * 2 = 0.8 0 0.8
    0.8 * 4 = 1.6 1 0.6
    0.6 * 2 = 1.2 1 0.2
    ... ... ...
  2. 基于IEEE74标准的双精度浮点数(尾数最多52位),我们可以得到如下结果:

    $$ 0.1 = 0.0001 (1001100110011001100110011001100110011001100110011001) (52) $$

  3. 规格化处理

    • 规格化定义(r表示阶基值,这里为2,S表示尾数)
      $$
      \frac{1}{2} \leq \vert{S}\vert\ < 1
      $$

    • 对于负数形式的补码,规格化的定义不适用(下面负数补码表示-1,不满足规格化定义)

      S>0 规格化形式 S<0 规格化形式
      真值 0.1XX...X 真值 -0.1XX...X
      原码 0.1XX...X 原码 1.1XX...X
      补码 0.1XX...X 补码 1.0XX...X
      反码 0.1XX...X 反码 1.0XX...X
    • 因此通常来说

      对于原码,不论整数,负数,第一数位为1即为规格化

      对于补码,符号位和第一数位不同为规格化

      在计算机中我们通常使用异或电路,当符号位和第一数位不同时,表示规格化完成

IEEE754标准

  • 数字存储格式:S(数符) + 阶码(含阶符) + 尾数
  • 尾数为规格化表示
  • 非“0”的有效位最高位为“1”(隐含)
符号位 S 阶码 尾数 总位数
短实数(单精度) 1 8 23 32
长实数(双精度) 1 11 52 64
临时实数 1 15 64 80

0.1和0.2在计算机中是如何存储的

上面我们求出了0.1的二进制表示,同理可以求出0.2的(52表示尾数为52位)
$$
\begin{aligned}
0.1 = 0.0001\quad&1001100110011001100110011001100110011001100110011001 (52)\
0.2 = 0.001\quad&1001100110011001100110011001100110011001100110011001 (52)
\end{aligned}
$$
计算机中浮点数阶码(P)一般使用移码表示,尾数(S)使用补码表示,小数点前一个1做隐含处理。求得0.1和0.2的阶码和尾数如下
$$
\begin{aligned}
S(0.1) = 1.10011001100...1 (1100\times12)\
S(0.2) = 1.100110011...001 (0011\times12)\
P(0.1) = 1,0000000100(-4)\
P(0.2) = 1,0000000011(-3)
\end{aligned}
$$

将阶数使用移码表示,存入计算机
$$
\begin{aligned}
0.1 \Rightarrow 0:01111111100:1001100110011001100110011001100110011001100110011010\
0.2 \Rightarrow 0:01111111101:1001100110011001100110011001100110011001100110011010\
more clear:\
0.1 = 2^{-4} \times [1].1001100110011001100110011001100110011001100110011010\
0.2 = 2^{-3} \times [1].1001100110011001100110011001100110011001100110011010
\end{aligned}
$$

0.1+0.2在计算机中是如何运算的

首先需要対阶,小阶向大阶看齐(小阶的尾数减小,只需右移,损失精度而不会造成错误),这里阶差为1

注意这里作为小阶的0.1右移添补的是隐含的“1”,而不是默认右移添0
$$
\begin{aligned}
0.1 = 2^{-3}\times&0.1100110011001100110011001100110011001100110011001101(0)\
0.2 = 2^{-3}\times&1.1001100110011001100110011001100110011001100110011010\
sum = 2^{-3}\times1&0.0110011001100110011001100110011001100110011001100111\
\end{aligned}
$$

IEEE754标准浮点数舍入模型

IEEE754标准对浮点数进行舍入时,一共定义了四种模型

Round to Nearest - roundTiesToEven (Default):向最近的数靠近,最近的数需满足最低有效位为0或者偶数

Round toward 0:向0靠近

Round toward +∞:向正无穷靠近

Round toward −∞:向负无穷靠近

第一种模型解决了50%的舍入情况,还有一种模型叫做Round to Nearest - tiesAwayFromZero,也就是靠近最低有效位为奇数的值,例子如下:

Example of rounding to integers using the IEEE 754 rules

Mode / Example Value +11.5 +12.5 −11.5 −12.5
to nearest, ties to even(默认模型) +12.0 +12.0 −12.0 −12.0
toward 0 +11.0 +12.0 −11.0 −12.0
toward +∞ +12.0 +13.0 −11.0 −12.0
toward −∞ +11.0 +12.0 −12.0 −13.0
to nearest, ties away from zero(非浮点数舍入模型) +12.0 +13.0 −12.0 −13.0

这时候我们再来看sum规格化后,是如何使用上述标准进行舍入的

误差的产生

首先,sum做规格化,并隐含1后如下(sum此时位于a,b之间):
$$
\begin{aligned}
a = 2^{-2}\times&1.0011001100110011001100110011001100110011001100110011(0)\
sum = 2^{-2}\times&1.0011001100110011001100110011001100110011001100110011(1)\
b=2^{-2}\times&1.0011001100110011001100110011001100110011001100110100(0)\
\end{aligned}
$$

按照上述第一个舍入模型,a的最低有效位为1,b的最低有效位为0,sum将使用b,然后存入计算机中。最后,我们将存入计算机中的0.3和sum进行一个比较
$$
\begin{aligned}
more clear:\
0.3 = 2^{-2}\times&1.0011001100110011001100110011001100110011001100110011\
sum=2^{-2}\times&1.0011001100110011001100110011001100110011001100110100\
\
0.1 + 0.2 = 0:01111111101&:0011001100110011001100110011001100110011001100110[100]\
0.3 = 0:01111111101&:0011001100110011001100110011001100110011001100110[011]
\end{aligned}
$$

最终结果相差了$2^{-2}\times2^{-52} = 2^{-54}$!!!

如果再将上面两个数转换成我们熟悉的十次方
$$
\begin{aligned}
0.1 + 0.2 = &0.300000000000000044408920985006...\
0.3 = &0.299999999999999988897769753748...
\end{aligned}
$$
控制台输出一下

var ll  = 0.300000000000000044408920985006; // 中间有15个0
var lll = 0.299999999999999988897769753748;
console.log(ll,lll); //answer: 0.30000000000000004 0.3

总结

  • 一道涉及JS基础数据结构—浮点数的问题,深入探究起来,复习了一波计算机组成原理知识

  • 当时书上对IEEE754标准说得也很少,甚至不知道IEEE754的舍入标准。以至于之前的笔记我都认为sum的舍入使用的是学过的0舍1入法。直到写下这篇文章,揭开IEEE 754的面纱,才发现没那么简单

  • 作为自己的第一篇文章,今后还有很多值得学习和努力的地方

参考

  1. Is floating point math broken? -- stack overflow(answer by Wai Ha Lee)

  2. Rounding floating-point numbers -- Wikipedia

  3. IEEE 754: Rounding Rules -- Wikipedia

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.