数值3.0按IEEE 754标准,在计算机数值中的表示形式为?

无论在java python javaScript里面都存在 1+ 2!== 3 问题这个问題的产生根源在于计算存储数字是二进制,对无限循环小数和无理数采用双精度64位double浮点数_float为32位,即52位小数+11位指数+1位符号超过52位小数溢出而產生精度丢失

在知乎上上看到如下问题:

1.该问题出现的原因 ?

2.为何其他编程语言比如java中可能没有js那么明显

3.大家在项目中踩过浮点数精度嘚坑?

4.最后采用哪些方案规避这个问题的

之前自己答的不是满意(对 还是满意的),想对这个问题做个深入浅出的总结

再看到这几篇长攵《》、《》、《》略有所悟,整理如下:

这个问题并不只是在Javascript中才会出现任何使用二进制浮点数的编程语言都会有这个问题,只不過在 C++/C#/Java 这些语言中已经封装好了方法来避免精度的问题而 JavaScript 是一门弱类型的语言,从设计思想上就没有对浮点数有个严格的数据类型所以精度误差的问题就显得格外突出。

JavaScript 中的数字类型只有 Number 一种Number 类型采用 IEEE754 标准中的 “双精度浮点数” 来表示一个数字,不区分整数和浮点数

什么是IEEE-745浮点数表示法

IEEE-745浮点数表示法是一种可以精确地表示分数的二进制示法,比如1/21/8,1/1024

十进制小数如何表示为转为二进制

即0.25的二进制为 0.01 ( 第┅次所得到为最高位,最后一次得到为最低位)

即0.8125的二进制是0.1101(第一次所得到为最高位,最后一次得到为最低位)

所以0.1转化成二进制是:0.01 1001…(无限循环)

同理0.2的二进制是0.11 0011…(无限循环)

是一种把一个数表示成a与10的n次幂相乘的形式(1≤a<10n为整数)的记数法。

例如:00=1.9计算器或电脑表達10的幂是一般是用E或e,也就是1.

科学记数法的形式是由两个数的乘积组成的。表示为a×10^b(aEb)其中一个因数为a(1≤|a|<10),另一个因数为10^n

运鼡科学记数法a×10^n的数字,它的精确度以a的最后一个数在原数中的数位为准

十进制的-5.0,写成二进制是-101.0相当于-1.01×2^2,推荐阅读《》

在二进制裏面即a×2^b,1≤a<2也就是说,a可以写成1.xxxxxx的形式其中xxxxxx表示小数部分。IEEE 754规定在计算机数值内部保存a时,默认这个数的第一位总是1因此可鉯被舍去,只保存后面的xxxxxx部分比如保存1.01的时候,只保存01等到读取的时候,再把第一位的1加上去这样做的目的,是节省1位有效数字鉯64位浮点数为例,留给a只有52位将第一位的1舍去以后,等于可以保存53位有效数字

IEEE-745浮点数表示法存储结构

在 IEEE754 中双精度浮点数采用 64 位存储,即 8 个字节表示一个浮点数 其存储结构如下图所示:

指数位可以通过下面的方法转换为使用的指数值:

IEEE-745浮点数表示法记录数值范围

从存储結构中可以看出, 指数部分的长度是11个二进制即指数部分能表示的最大值是 2047(2^11-1)

取中间值进行偏移,用来表示负指数也就是说指数的范围是 [-] 

因此,这种存储结构能够表示的数值范围为 2^1024 到 2^-1023 超出这个范围的数无法表示 。2^1024  和 2^-1023  转换为科学计数法如下所示:

IEEE-745浮点数表示法数值精喥

在 64 位的二进制中符号位决定了一个数的正负,指数部分决定了数值的大小小数部分决定了数值的精度

IEEE754 规定有效数字第一位默认總是1 。因此在表示精度的位数前面,还存在一个 “隐藏位” 固定为 1 ,但它不保存在 64 位浮点数之中也就是说,有效数字总是 1.xx...xx 的形式其中 xx..xx 的部分保存在 64 位浮点数之中,最长为52位 所以,JavaScript 提供的有效数字最长为 53 个二进制位其内部实际的表现形式为:

对于超过这个范围的整数,JavaScript 依旧可以进行运算但却不保证运算结果的精度。

IEEE-745浮点数表示法数值精度丢失

计算机数值中的数字都是以二进制存储的二进制浮點数表示法并不能精确的表示类似0.1这样 的简单的数字

如果要计算 0.1 + 0.2 的结果,计算机数值会先把 0.1 和 0.2 分别转化成二进制然后相加,最后再把相加得到的结果转为十进制 

但有一些浮点数在转化为二进制时会出现无限循环 。比如 十进制的 0.1 转化为二进制,会得到如下结果:

而存储結构中的尾数部分最多只能表示 53 位为了能表示 0.1,只能模仿十进制进行四舍五入了但二进制只有 0 和 1 , 于是变为 0 舍 1 入 因此,0.1 在计算机数徝里的二进制表示形式如下:

用标准计数法表示如下:

在计算浮点数相加时需要先进行 “对位”,将较小的指数化为较大的指数并将尛数部分相应右移:

最终,“0.1 + 0.2” 在计算机数值里的计算过程如下:

经过上面的计算过程0.1 + 0.2 得到的结果也可以表示为:

通过 JS 将这个二进制结果转化为十进制表示:

这是一个典型的精度丢失案例,从上面的计算过程可以看出0.1 和 0.2 在转换为二进制时就发生了一次精度丢失,而对于計算后的二进制又有一次精度丢失 因此,得到的结果是不准确的

几乎所有的编程语言浮点数都是都采用IEEE浮点数算术标准
  1. long与double在java中本身都昰用64位存储的,但是他们的存储方式不同,导致double可储存的范围比long大很多

  2. long可以准确存储19位数字而double只能准备存储16位数字(实际测试,是17位)。double由于有exp位可以存16位以上的数字,但是需要以低位的不精确作为代价如果一个大于17位的long型数字存到double上,就会丢失数字末尾的精度

  3. 如果需要高于19位数字的精确存储则必须用BigInteger来保存,当然会牺牲一些性能

java 基本数据类型

 java中char类型占2个字节、16位可以存放汉子,字母和数字占一個字节一个字节8位,中文占2个字节16位。

double是n*2^m(n乘以2的m次方)这种形式存储的只需要记录n和m两个数就行了,m的值影响范围大所以表示的范圍比long大。

但是m越大n的精度就越小,所以double并不能把它所表示的范围里的所有数都能精确表示出来而long就可以。

  • float浮点数小数点后第7位是部汾准确的。例如1.0000004就是1.通过得到的,其实际保存和1.0000003相同1.0000006也是通过舍入得到的。再往前第6位及以后均可以通过小数准确表示出来通常说float數据的有效位是6~7位,也是这个原因一般来说,无论是整数或者小数用float表示时,从左边第一个非0的数字算起从高到低的7位是准确的。此后的数位是不能保证精确的

  • double双精度浮点数小数部分有52位,和上面类似最低6位(2^-52,2^-51,......)表示的规格化小数如下所示。从图中可以看出双精度浮点数能准确表示到小数点后第15位,第16位部分准确用double表示时,从左边第一个非0的数字起从高到低的16位是准确的,此后的数位不一定精確

尽管浮点数表示的范围很广但由于精度损失的存在,加上幂次的放大作用一个浮点数实际上是表示了周围的一个有理数区间。如果將浮点数绘制到一个数轴上直观上看,靠近0的部分浮点数出现较密集。越靠近无穷大浮点数分布越稀疏,一个浮点值代表了周围一爿数据如下图所示。从这个意义上来说浮点数不宜直接比较相等,它们是代表了一个数据范围实际应用中,如果要使用浮点数计算一定要考虑精度问题。在满足精度要求的前提下计算结果才是有效的。 

在计算精度要求情形下例如商业计算等,应该避免使用浮点數严格采取高精度计算。

我们常用的分数(特别是在金融的计算方面)都是十进制分数1/101/100等。或许以后电路设计或许会支持十进制数字類型以避免这些舍入问题在这之前,你更愿意使用大整数进行重要的金融计算例如,要使用整数‘分’而不是使用小数‘元’进行货仳单位的运算

即在运算前我们把参加运算的数先升级(10的X的次方)到整数等运算完后再降级(0.1的X的次方)。

(一般指8421BCD码形式)亦称二进码十进数或二-┿进制代码用4位二进制数来表示1位十进制数中的0~9这10个数。一般用于高精度计算比如会计制度经常需要对很长的数字串作准确的计算。楿对于一般的浮点式记数法采用BCD码,既可保存数值的精确度又可免去使电脑作浮点运算时所耗费的时间。

  1. 二进制在电路设计中物理上哽易实现因为电子器件大多具有两种稳定状态,比如晶体管的导通和截止电压的高和低,磁性的有和无等而找到一个具有十个稳定狀态的电子器件是很困难的。

  2. 二进制规则简单十进制有55种求和与求积的运算规则,二进制仅有各有3种这样可以简化运算器等物理器件嘚设计。另外计算机数值的部件状态少,可以增强整个系统的稳定性

  3. 与逻辑量相吻合。二进制数0和1正好与逻辑量“真”和“假”相对應因此用二进制数表示二值逻辑显得十分自然。

  4. 可靠性高二进制中只使用0和1两个数字,传输和处理时不易出错因而可以保障计算机數值具有很高的可靠性

我觉得主要还是因为第一条。如果比如能够设计出十进制的元器件那么对于设计其运算器也不再话下。

JS数字精度丟失的一些典型问题

再问问一个问题 :在js数字类型中浮点数的最高精度多少位小数(16位 or 17位?……why

  1. IEEE754 规定,有效数字第一位默认总是1 因此,在表示精度的位数前面还存在一个 “隐藏位” ,固定为 1 但它不保存在 64 位浮点数之中。也就是说有效数字总是 1.xx...xx 的形式,其中 xx..xx 的部汾保存在 64 位浮点数之中最长为52位 。所以JavaScript 提供的有效数字最长为 53 个二进制位

}
版权声明:本文为博主原创文章遵循 版权协议,转载请附上原文出处链接和本声明

IEEE 754是美国电气电子工程师协会通过地一个标准,用于在计算机数值上表示浮点数Java采鼡32位IEEE 754表示float型,64位IEEE 754表示doubl型 IEEE 标准中,浮点数是将特定长度的连续字节的所有二进制位分割为特定宽度的符号域指数域和尾数域三个域,其中保存的值分别用于表示给定二进制浮点数中的符号指数和尾数。这样通过尾数和可以调节的指数(所以称为"浮点")就可以表达给萣的数值了,比如 123.45 用十进制科学计数法可以表达为 1.2345 102 其中 1.2345 为尾数,10 为基数为指数。浮点数利用指数达到了浮动小数点的效果从而可以靈活地表达更大范围的实数。

具体的格式参见下面的图例:

    float和double类型在java中执行地是二进制浮点运算这是为了在广泛的数值范围上提供较为精确的快速近似技术安而精心设计的。然而它们并没有提供完全精确的结果,只要是超过精度能表示的范围就会产生误差往往产生误差不是 因为数的大小,而是因为数的精度因此,产生的结果接近但不等于想要的结果尤其在使用 float  double 作精确运算的时候要特别小心。

102洇为这种多样性,有必要对其加以规范化以达到统一表达的目标规范的(Normalized)浮点数表达方式具有如下形式:

基于规范表达的浮点数对应嘚具体值可由下面的表达式计算而得:

对于十进制的浮点数,即基数 β 等于 10 的浮点数而言上面的表达式非常容易理解,也很直白计算機数值内部的数值表达是基于二进制的。从上面的表达式我们可以知道,二进制数同样可以有小数点也同样具有类似于十进制的表达方式。只是此时 β 等于 2而每个数字 d 只能在 0 和 1 之间取值。比如二进制数  相当于 1

因此按单精度格式表示为:

同理按双精度格式表示为:

可以栲虑采用一些替代方案来实现如通过 

}
版权声明:本文为博主原创文章遵循 版权协议,转载请附上原文出处链接和本声明

最近开始看数值分析的书籍,在此做些笔记!

本节介绍一个关于浮点数的计算机数徝计算模型IEEE 754浮点标准IEEE(电器和电子工程师协会)对于建立行业标准有着积极的兴趣,他们的浮点运算格式已经成为整个计算机数值行业中单精度和双精度运算的共同标准

当使用有限精度的计算机数值存储单元来表示无限精度的实数时,舍入误差是不可避免的尽管我们希望茬很长的运算中产生的小误差对答案只会产生很小的影响,但是在很多情形下这事实上是一种一厢情愿的想法

IEEE标准包含一组实数的二进淛表示法。浮点数由三部分组成即符号(sign,+或-)、尾数(mantissa它包含一串有效数字)以及阶(即指数,exponent)这三部分合一起就表示计算机數值中的浮点数。

浮点数由三种常用的精度等级即单精度,双精度加长精度。浮点数在这三种格式里的位数分别是32,64,80每部分中位数的劃分如下表:

这三类精度的用法实质上是相同的。IEEE浮点数的正规化形式是:

其中N个b中的每一位是0或是1,p是一个M位二进制数表示的指数

當一个二进制数作为正规浮点数存储时最左边的1刚好位于小数点的左边,通过改变阶来调整移位十进制9的二进制表示是1001,应该以这种形式存储

我们只讨论双精度,其他类似在由许多C编译程序和MatlAB使用的双精度中,M=11N=52.

这是双精度1等于尾数0有52位。下一个比1大的浮点数等于或鍺是

}

我要回帖

更多关于 计算机数值 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信