详谈javascript精度问题与调整

一个经典的问题:

0.1+0.2==0.3

答案是:false

因为:0.1+0.2=0.30000000000000004

第一次看到这个结果就是无比惊讶,下巴碰到地上,得深入了解下问题出在哪里,该怎么去调整。

产生问题的原因

在JS中数值类型就只有number类型,没有int,float,double之分,number类型实际上存储的就是IEEE754标准的浮点数,计算规则也是。

在表达式计算前,先要按照标准将两个数转成浮点数。

IEEE 754规定:

1.32位的浮点数(单精度),最高的1位是符号位S,接着的8位是指数E,剩下的23位为有效数字M。

浮点数的表现形式:

x=(-1)^S*m*2^(e+127)

m=1.M

E=e+127

2.64位的浮点数(双精度),最高的1位是符号位S,接着的11位是指数E,剩下的52位为有效数字M。

浮点数的表现形式:

x=(-1)^S*m*2^(e+1023)

m=1.M

E=e+1023

我们就按照双精度浮点数的标准转一下看看。

首先按照规则将0.1转成二进制的浮点数。

0.1*2=0.2 //0
0.2*2=0.4 //00
0.4*2=0.8 //000
0.8*2=1.6 //0001
0.6*2=1.2 //00011
0.2*2=0.4 //000110
0.4*2=0.8 //0001100
0.8*2=1.6 //00011001
0.6*2=1.2 //000110011
0.2*2=0.4 //0001100110
0.4*2=0.8 //00011001100
0.8*2=1.6 //000110011001
0.6*2=1.2 //0001100110011
0.2*2=0.4 //00011001100110
0.4*2=0.8 //000110011001100
0.8*2=1.6 //0001100110011001
0.6*2=1.2 //00011001100110011
//省略

在转换中,会发现小数位的二进制值在不停的重复,转换没完没了了,因为乘不尽啊,不是10的倍数。

转换也不可能一直重复下去,按照标准规格化的要求凑满。

转换结果:

0.00011001100110011001100110011001100110011001100110011001

精度问题产生的第一个原因就在这里诞生了,按照标准算出来的二进制浮点数并不能都精确的表示一个小数,只是无限近似,0.5可以,因为5是10的倍数,转出来的小数位二进制不会重复。

我们看看再转回小数会怎么样,按照公式写成:

0*2^-1 + 0*2^-2 + 0*2^-3 + 1*2^-4 + 1*2^-5 + 0*2^-6 + 0*2^-7 + 1*2^-8 + 1*2^-9 + 0*2^-10 + 0*2^-11 + 1*2^-12 + 1*2^-13 + 0*2^-14 + 0*2^-15 + 1*2^-16 + 1*2^-17 + 0*2^-18 + 0*2^-19 + 1*2^-20 + 1*2^-21 + 0*2^-22 + 0*2^-23 + 1*2^-24 + 1*2^-25 + 0*2^-26 + 0*2^-27 + 1*2^-28 + 1*2^-29 + 0*2^-30 + 0*2^-31 + 1*2^-32 + 1*2^-33 + 0*2^-34 + 0*2^-35 + 1*2^-36 + 1*2^-37 + 0*2^-38 + 0*2^-39 + 1*2^-40 + 1*2^-41 + 0*2^-42 + 0*2^-43 + 1*2^-44 + 1*2^-45 + 0*2^-46 + 0*2^-47 + 1*2^-48 + 1*2^-49 + 0*2^-50 + 0*2^-51 + 1*2^-52 + 1*2^-53 + 0*2^-54 + 0*2^-55 + 1*2^-56

计算结果:

0.09999999999999999167332731531133

精度就在这里丢了一次。就是转换成小数位的二进制的时候。

按照表现形式的要求,要写成x=(-1)^s*m*2^(e+1023),m=1.M的格式,按照要求尾数m的左边最高位总是1,所以要上面小数二进制结果的小数点进行移动

移动前:

0.00011001100110011001100110011001100110011001100110011001

移动后:

1.1001100110011001100110011001100110011001100110011001*2^-4

小数点右边选取要求的52位,上面的结果因为是提前算好,所以就省略了截取工作。

因为小数点最左侧的最高位总是1,所以它是不用存储的,那么虽然存储的是52位,但实际上可以表示53位的浮点数。

S=0,E=-4+1023=1019,m=1.M=1.1001100110011001100110011001100110011001100110011001,M=1001100110011001100110011001100110011001100110011001

浮点数表示:

x=-1^0*1.1001100110011001100110011001100110011001100110011001*2^1019

浮点数存储值(最高的1位是符号位S,接着的11位是指数E,剩下的52位为有效数字M):

0 ‭001111111011‬ 1001100110011001100110011001100110011001100110011001

同理0.2的IEEE754的转换后的结果:

浮点数表示:

-1^0*1.1001100110011001100110011001100110011001100110011001*2^1020

浮点数存储值(最高的1位是符号位S,接着的11位是指数E,剩下的52位为有效数字M):

0 ‭001111111100‬ 1001100110011001100110011001100110011001100110011001

接下来,按照IEEE754的加法规则,运算过程为:

1.0操作数的检查。

2.比较阶码大小并对阶。

3.尾数进行加法运算。

4.结果规格化。

5.舍入处理。

6.溢出处理。

按照计算过程,结果规格化、舍入处理、溢出处理都会遭成精度问题。

总结来看,造成精度问题的环节:

1.小数向二进制转换。

2.运算过程中的规格化,舍入、溢出处理。

精度调整

两种方法可以进行调整。

1.使用toFixed函数对小数位进行四舍五入。

但是其返回值是字符串,其参数是0 ~ 20之间的值,需要注意。

(0.1+0.2).toFixed(1) // '0.3'

2.无小数运算,运算结果附上小数点

使用该方法,要注意因为要变成整数再计算,对于一个小数点后位数很多的数来运算的时候,要注意溢出。

//加
function add(arg1,arg2){
 var digits1,digits2,maxDigits;
 try{digits1=arg1.toString().split(".")[1].length}catch(e){digits1=0}
 try{digits2=arg2.toString().split(".")[1].length}catch(e){digits2=0}
 maxDigits=Math.pow(10,Math.max(digits1,digits2))
 return (arg1*maxDigits+arg2*maxDigits)/maxDigits
} 

//减
function sub(arg1,arg2){
 var digits1,digits2,maxDigits;
 try{digits1=arg1.toString().split(".")[1].length}catch(e){digits1=0}
 try{digits2=arg2.toString().split(".")[1].length}catch(e){digits2=0}
 maxDigits=Math.pow(10,Math.max(digits1,digits2));
 return (arg1*maxDigits-arg2*maxDigits)/maxDigits;
} 

//乘
function mul(arg1,arg2) {
 var digits=0,s1=arg1.toString(),s2=arg2.toString();
 try{digits+=s1.split(".")[1].length}catch(e){}
 try{digits+=s2.split(".")[1].length}catch(e){}
 return Number(s1.replace(".",""))*Number(s2.replace(".",""))/Math.pow(10,digits);
}

//除
function div(arg1,arg2){
 var int1=0,int2=0,digits1,digits2;
 try{digits1=arg1.toString().split(".")[1].length}catch(e){digits1=0}
 try{digits2=arg2.toString().split(".")[1].length}catch(e){digits2=0} 

 int1=Number(arg1.toString().replace(".",""))
 int2=Number(arg2.toString().replace(".",""))
 return (int1/int2)*Math.pow(10,digits2-digits1); 

}

以上这篇详谈javascript精度问题与调整就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • JS长整型精度问题实例分析

    本文实例分析了一个JS长整型精度问题.分享给大家供大家参考.具体分析如下: 问题描述: 后台有一个脚本功能,可编写脚本动态调用Java代码 复制代码 代码如下: importClass(com.ztgame.center.controller.api,P360ApiController);  var roleId = 10214734953631045;  p360ApiController.notice(roleId, 4); 脚本执行成功,但运行结果与设定不一样,此人没有收到邮件 查看日志 

  • 浅谈JavaScript中小数和大整数的精度丢失

    先来看两个问题: 0.1 + 0.2 == 0.3; // false 9999999999999999 == 10000000000000000; // true 第一个问题是小数的精度问题,在业界不少博客里已有讨论.第二个问题,去年公司有个系统的数据库在做数据订正时,发现有部分数据重复的诡异现象.本文将从规范出发,对上面的问题做个小结. 最大整数 JavaScript 中的数字是用 IEEE 754 双精度 64 位浮点数 来存储的,其格式为: s x m x 2^e s 是符号位,表示正负

  • 解决JavaScript数字精度丢失问题的方法

    本文分为三个部分 JS 数字精度丢失的一些典型问题 JS 数字精度丢失的原因 解决方案(一个对象+一个函数) 一.JS数字精度丢失的一些典型问题 1. 两个简单的浮点数相加 0.1 + 0.2 != 0.3 // true 这真不是 Firebug 的问题,可以用alert试试 (哈哈开玩笑). 看看Java的运算结果 再看看Python 2. 大整数运算 16位和17位数竟然相等,没天理啊. 又如 var x = 9007199254740992 x + 1 == x // ? 看结果 三观又

  • js计算精度问题小结

    复制代码 代码如下: //问题比如:7*0.8 JavaScript算出来就是:5.6000000000000005 //加法函数,用来得到精确的加法结果            //说明:javascript的加法结果会有误差,在两个浮点数相加的时候会比较明显.这个函数返回较为精确的加法结果.            //调用:accAdd(arg1,arg2)            //返回值:arg1加上arg2的精确结果            function accAdd(arg1, ar

  • 详谈javascript精度问题与调整

    一个经典的问题: 0.1+0.2==0.3 答案是:false 因为:0.1+0.2=0.30000000000000004 第一次看到这个结果就是无比惊讶,下巴碰到地上,得深入了解下问题出在哪里,该怎么去调整. 产生问题的原因 在JS中数值类型就只有number类型,没有int,float,double之分,number类型实际上存储的就是IEEE754标准的浮点数,计算规则也是. 在表达式计算前,先要按照标准将两个数转成浮点数. IEEE 754规定: 1.32位的浮点数(单精度),最高的1

  • 详谈JavaScript 匿名函数及闭包

    1.匿名函数 函数是JavaScript中最灵活的一种对象,这里只是讲解其匿名函数的用途.匿名函数:就是没有函数名的函数. 1.1 函数的定义,首先简单介绍一下函数的定义,大致可分为三种方式 第一种:这也是最常规的一种 复制代码 代码如下: function double(x){     return 2 * x;   } 第二种:这种方法使用了Function构造函数,把参数列表和函数体都作为字符串,很不方便,不建议使用. 复制代码 代码如下: var double = new Functio

  • 详谈javascript中的cookie

    JavaScript中的另一个机制:cookie,则可以达到真正全局变量的要求. cookie是浏览器 提供的一种机制,它将document 对象的cookie属性提供给JavaScript.可以由JavaScript对其进行控制,而并不是JavaScript本身的性质. cookie概述 在上一节,曾经利用一个不变的框架来存储购物栏数据,而商品显示页面是不断变化的, 尽管这样能达到一个模拟 全局变量的功能,但并不严谨.例如在导航框架页面内右击,单击快捷菜单中的[刷新]命令,则所有的JavaSc

  • 详谈JavaScript内存泄漏

    1.什么是闭包.以及闭包所涉及的作用域链这里就不说了. 2.JavaScript垃圾回收机制 JavaScript不需要手动地释放内存,它使用一种自动垃圾回收机制(garbage collection).当一个对象无用的时候,即程序中无变量引用这个对象时,就会从内存中释放掉这个变量. 复制代码 代码如下: var s = [ 1, 2 ,3];     var s = null;     //这样原始的数组[1 ,2 ,3]就会被释放掉了. 3.循环引用 三个对象 A .B .C AàBàC :

  • 详谈javascript异步编程

    异步编程带来的问题在客户端Javascript中并不明显,但随着服务器端Javascript越来越广的被使用,大量的异步IO操作使得该问题变得明显.许多不同的方法都可以解决这个问题,本文讨论了一些方法,但并不深入.大家需要根据自己的情况选择一个适于自己的方法. 本文为大家详细介绍js中的异步编程,具体内容如下 一 关于事件的异步 事件是JavaScript中最重要的一个特征,nodejs就是利用js这一异步而设计出来的.所以这里讲一下事件机制. 在一个js文件中,如果要运行某一个函数,有2中手段

  • 详谈浮点精度(float、double)运算不精确的原因

    目录 为什么浮点精度运算会有问题 精度运算丢失的解决办法 拓展:详解浮点型 为什么浮点精度运算会有问题 我们平常使用的编程语言大多都有一个问题--浮点型精度运算会不准确.比如 double num = 0.1 + 0.1 + 0.1; // 输出结果为 0.30000000000000004 double num2 = 0.65 - 0.6; // 输出结果为 0.05000000000000004 笔者在测试的时候发现 C/C++ 竟然不会出现这种问题,我最初以为是编译器优化,把这个问题解决了

  • 详谈JavaScript的闭包及应用

    闭包真的是学过一遍又一遍,Js博大精深,每次学习都感觉有新的收获.相信在大家封装前端插件时,闭包是必不可少的.闭包的真正好处我个人认为除了封装还是封装,能带个我们私有方法,和调用上的灵活方便,也会使你的代码对外的对象保持干净整洁. 进入正题 维基百科这样定义了JS闭包:在计算机科学中,闭包(英语:Closure),又称词法闭包(Lexical Closure)或函数闭包(function closures),是引用了自由变量的函数.这个被引用的自由变量将和这个函数一同存在,即使已经离开了创造它的

  • 详谈javascript中DOM的基本属性

    结构和内容属性 nodeType 所有的节点都有类型,节点总共有以下的12种类型. 复制代码 代码如下: interface Node { // NodeType const unsigned short      ELEMENT_NODE       = 1; const unsigned short      ATTRIBUTE_NODE     = 2; const unsigned short      TEXT_NODE          = 3; const unsigned sho

  • 用javascript动态调整iframe高度的代码

    当你在页面上使用了iframe之后,一般来说会不希望iframe显示难看的滚动条,以使iframe里面的内容和主页面的内容浑然一体.这时候你会设置 scrolling="no" 属性.但是这样一来如果iframe里面的内容是变化的,高度会随之内容的变化而变化的时候,你的iframe就会显得太长导致底下一大片空白,或者正好相反,由于iframe的高度太小导致一部分内容会被挡住.这里我提供一个兼容IE/NS/Firefox的javascript脚本实现动态调整iframe的高度.如果需要调

  • 用javascript动态调整iframe高度的方法

    当你在页面上使用了iframe之后,一般来说会不希望iframe显示难看的滚动条,以使iframe里面的内容和主页面的内容浑然一体.这时候你会设置 scrolling="no" 属性.但是这样一来如果iframe里面的内容是变化的,高度会随之内容的变化而变化的时候,你的iframe就会显得太长导致底下一大片空白,或者正好相反,由于iframe的高度太小导致一部分内容会被挡住.这里我提供一个兼容IE/NS/Firefox的javascript脚本实现动态调整iframe的高度.如果需要调

随机推荐