浅谈JavaScript中小数和大整数的精度丢失.
- 格式:pdf
- 大小:198.19 KB
- 文档页数:2
java,js中⼩数计算精度误差问题 在碰见这个问题之前我完全没有这个概念,碰见了特此记录; 项⽬js⾥⾯中⽤到了number*0.2(其中number是⼀个整数,我测试的时候是259),得到的结果却是51.80000000000000004这么个结果, 当时直接蒙逼,根本不知道什么原因,随⼿在java中写了⼀个System.out.println(259*0.2),得到⼀样的结果; 这个时候觉得就不是数据类型的问题了,上⽹围观⼤神给出的原因--------------------------------------------------------------------------------------------------------------------------------------------------- ⽹上有这样⼀个问题:0.1+0.2在计算机中的结果 不论java,javascript,python中的结果都是0.30000000000000004计算机中存储的都是⼆进制的0和1,⽽我们现实中的数存⼊计算机中转换为⼆进制时有可能不能整除,也就是不能正好整除,所以⽤⼆进制表⽰现实中的数并计算就产⽣了误差。
把⼗进制的0.1和0.2转换为⼆进制:0.1 => 0.0001 1001 1001 1001…(1001⽆限循环)0.2 => 0.0011 0011 0011 0011…(0011⽆限循环)但是我们计算机的硬件存储的位数是有限制的不可能⽆限循环下去,⼀般双精度浮点数总共占⽤64位,、其中最多53位为有效精度数字(包括符号位),所以存储时:0.1=>0.0001 1001 1001 1001 1001 1001 1001 1001 1001 1001 1001 1001 10010.2=>0.0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 0011 00110.1+0.2=>0.0100 1100 1100 1100 1100 1100 11001 100 1100 1100 1100 1100 1100转换为⼗进制就是:0.30000000000000004。
下面小编就为大家带来一篇浅谈JavaScript中小数和大整数的精度丢失。
小编觉得挺不错的,现在就分享给大家,也给大家做个参考。
先来看两个问题:0.1 + 0.2 == 0.3; // false9999999999999999 == 10000000000000000; // true第一个问题是小数的精度问题,在业界不少博客里已有讨论。
第二个问题,去年公司有个系统的数据库在做数据订正时,发现有部分数据重复的诡异现象。
本文将从规范出发,对上十进制0.1 的二进制为0.0 0011 0011 0011 …(循环0011)十进制0.2 的二进制为0.0011 0011 0011 …(循环0011)0.1 + 0.2 相加可表示为: e = -4; m = 1.10011001100...1100(52 位) + e = -3; m = 1.10011001100...1100(52 位)--------------------------------------------- e = -3; m = 0.11001100110 (0110) + e = -3; m = 1.10011001100 (1100)--------------------------------------------- e = -3; m = 10.01100110011 (001)--------------------------------------------- = 0.01001100110011 (001) = 0.30000000000000004(十进制)根据上面的演算,还可以得出一个结论:当十进制小数的二进制表示的有限数字不超过52 位时,在JavaScript 里是可以精确存储的。
js 精度丢失原理
JS中精度丢失的原理主要有以下几个方面:
1. 浮点数的表示精度有限:在JS中,基本数据类型Number使用的是双精度浮点型,也就是其他语言中的double类型。
这种数据类型在内存中是以一定的位数来存储的,由于表示精度有限,对于某些无法精确表示的十进制小数,计算机在将其转换为二进制小数时会存在舍入误差,导致精度丢失。
2. 舍入误差的累积:在进行一系列浮点数算术运算时,舍入误差可能会累积并导致精度丢失。
每一次运算都会引入一些误差,这些误差在多次运算中逐渐累积,导致最终结果的精度降低。
3. 比较运算的不精确性:由于浮点数的表示精度有限,直接比较浮点数可能会导致不准确的结果。
微小的舍入误差可能使得两个看似相等的浮点数在比较时被认为是不等的。
4. 数值范围的限制:浮点数的表示范围是有限的,超出范围的数值可能会导致溢出或下溢,进而影响计算结果的精度。
了解JS中精度丢失的原理有助于更好地理解和解决相关问题,特别是在进行高精度计算或处理金融数据时尤为重要。
关于javascript小数精度丢失的完美解决方法介绍原因:js按照2进制来处理小数的加减乘除,在arg1的基础上将arg2的精度进行扩展或逆扩展匹配,所以会出现如下情况.javascript(js)的小数点加减乘除问题,是一个js的bug如0.3*1=0.2999999999等,下面列出可以完美求出相应精度的四种js 算法functionaccDiv(arg1,arg2){vart1=0,t2=0,r1,r2;try{t1=arg1.to String().split(".")[1].length}catch(e){}try{t2=arg2.toString().split(". ")[1].length}catch(e){}with(Math){r1=Number(arg1.toString().repl ace(".",""))r2=Number(arg2.toString().replace(".",""))returnaccMu l((r1/r2),pow(10,t2-t1));}}//乘法functionaccMul(arg1,arg2){varm=0,s1=arg1.toString(),s2=arg2.t oString();try{m+=s1.split(".")[1].length}catch(e){}try{m+=s2.split( ".")[1].length}catch(e){}returnNumber(s1.replace(".",""))*Number( s2.replace(".",""))/Math.pow(10,m)}//加法functionaccAdd(arg1,arg2){varr1,r2,m;try{r1=arg1.toString().split (".")[1].length}catch(e){r1=0}try{r2=arg2.toString().split(".")[1].len gth}catch(e){r2=0}m=Math.pow(10,Math.max(r1,r2))return(arg1* m+arg2*m)/m}//减法functionSubtr(arg1,arg2){varr1,r2,m,n;try{r1=arg1.toString().split (".")[1].length}catch(e){r1=0}try{r2=arg2.toString().split(".")[1].len gth}catch(e){r2=0}m=Math.pow(10,Math.max(r1,r2));n=(r1>=r2) r1:r2;return((arg1*m-arg2*m)/m).toFixed(n);}以上这篇javascript小数精度丢失的完美解决方法就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持。
JS加减乘除精度丢失问题解决⼀、前⾔⼯作中经常遇到⽤户输⼊后在前端实时计算结果,⽐如输⼊单价和数量后⾃动计算总价,部分情况下会出现丢失精度的问题。
解决⽅式为将⼩数放⼤为整数,计算完后再缩⼩。
⼆、解决/** 加法* add(0.123 , 1.4567 , 10.56789)*/function add(...val) {let sum = 0,maxDecimalLength = getMaxDecimalLength(...val)val.forEach((x, index) => {// 所有数值转为整数计算sum += Math.round(x * Math.pow(10, maxDecimalLength))})return sum / Math.pow(10, maxDecimalLength)}/** 减法* subtract(0.123 , 1.4567 , 10.56789)*/function subtract(...val) {let sum,maxDecimalLength = getMaxDecimalLength(...val)val.forEach((x, index) => {let nurVal = Math.round(x * Math.pow(10, maxDecimalLength));if (index === 0)sum = nurValelsesum -= nurVal})return sum / Math.pow(10, maxDecimalLength)}/** 乘法* multiply(0.123 , 1.4567 , 10.56789)*/function multiply(...val) {let sum,decimalLengthSum = 0val.forEach((x, index) => {// 获取当前⼩数位长度let decimalLength = getMaxDecimalLength(x)// 将当前数变为整数let nurVal = Math.round(x * Math.pow(10, decimalLength));decimalLengthSum += decimalLengthif (index === 0)sum = nurValelsesum *= nurVal})return sum / Math.pow(10, decimalLengthSum)}/** 除法* divide(0.123 , 1.4567 , 10.56789)*/function divide(...val) {let sum = 0,decimalLengthSum = 0val.forEach((x, index) => {// 获取当前⼩数位长度let decimalLength = getMaxDecimalLength(x)// 将当前数变为整数let nurVal = Math.round(x * Math.pow(10, decimalLength)); if (index === 0) {decimalLengthSum = decimalLengthsum = nurVal} else {decimalLengthSum -= decimalLengthsum /= nurVal}})return sum / Math.pow(10, decimalLengthSum)}/** 获取⼩数位数*/function getMaxDecimalLength(...val) {// 最⼤⼩数位长度let maxDecimalLength = 0val.forEach((x) => {const strVal = x.toString(),dotIndex = strVal.indexOf('.')if (dotIndex > -1) {// 获取当前值⼩数位长度let curDecimalLength = strVal.length - 1 - dotIndexif (curDecimalLength > maxDecimalLength) {maxDecimalLength = curDecimalLength}}})return maxDecimalLength}。
js计算精度丢失的解决方法嘿,朋友们!咱今儿来聊聊 js 计算精度丢失这档子事儿。
你说这玩意儿就跟那调皮的小猴子似的,时不时就蹦出来给你捣捣乱。
咱先搞清楚为啥会有这情况呀。
就好比你要把一块大蛋糕切成好多好多小块,这中间难免就会有些误差嘛。
js 在处理一些小数计算的时候,就可能会出现这种类似的小偏差。
那咋解决呢?嘿,这就有招儿啦!咱可以用一些巧妙的方法来对付它。
比如说,咱可以把小数转化成整数来计算呀,就像你把那小蛋糕先整个儿的算好了,再去分小块,这样不就减少误差啦?或者呢,咱可以用一些专门的库,就像是给咱的计算找了个厉害的助手,让它来帮忙搞定那些容易出问题的地方。
你想想看呀,如果咱不解决这个问题,那可就麻烦啦!就好比你要去买东西,本来应该给人家十块钱,结果因为计算精度丢失,你给了人家九块九或者十块一,那多不合适呀!这要是在一些重要的场合,比如涉及到钱财交易或者数据统计啥的,那可不得了!咱还可以通过一些小技巧来检查和发现这些问题呢。
就像医生给病人做检查一样,得找到病根儿才能对症下药呀。
比如说,咱可以在计算前后对比一下结果,看看是不是有啥不对劲的地方。
还有啊,咱得养成一个好习惯,就是对计算结果要多留个心眼儿。
别一股脑儿就相信了,得好好琢磨琢磨,看看是不是合理。
这就好比你走路,得时不时瞅瞅脚下,别一不小心踩坑里啦!总之呢,解决 js 计算精度丢失这个问题,就像是给咱的代码世界打扫卫生,把那些脏东西都清理掉,让咱的程序跑得更顺畅,更准确。
咱可不能小瞧了这个事儿,得认真对待,不然它可会给咱惹出大麻烦的哟!你说是不是这个理儿呀?所以呀,咱可得把这些方法都记住,遇到问题就拿出来用,让咱的代码变得乖乖的,不再捣乱啦!。
Js四则运算精度问题处理JavaScript⼩数在做四则运算时,精度会丢失,这会在项⽬中引起诸多不便。
先看个具体的例⼦://较⼩的数运算console.log(0.09999999 + 0.00000001); //0.09999999999999999console.log(-0.09999999 - 0.00000001); //-0.09999999999999999console.log(0.012345 * 0.000001); //1.2344999999999999e-8console.log(0.000001 / 0.0001); //0.009999999999999998//较⼤的数运算console.log(999999999 * 111111111); //111111110888888900从上⾯的结果可以看出,都不是正确的。
为了解决浮点数运算不准确的问题,在运算前我们把参加运算的数先升级(10的X的次⽅)到整数,等运算完后再降级(0.1的X的次⽅)。
具体的操作如下:/*** 四则运算** @param x* @param y* @param op 操作符,0:加;1:减;2:乘;3:除* @param acc 保留⼩数位个数,进⾏四舍五⼊*/function execute(x, y, op, acc) {var xx = Number(x == undefined ? 0 : x);var yy = Number(y == undefined ? 0 : y);//var a = science(xx);var b = science(yy);var na = a.r1;var nb = b.r1;var ta = a.r2;var tb = b.r2;var maxt = Math.max(ta, tb);//精度值处理var result = 0;switch (parseInt(op, 10)) {case0: //加result = (xx * maxt + yy * maxt) / maxt;break;case1: //减result = (xx * maxt - yy * maxt) / maxt;break;case2: // 乘result = na * nb / (ta * tb);break;case3: // 除result = na / nb * (tb / ta);default:}//⼩数位数处理if (acc) {return Number(Number(result).toFixed(parseInt(acc)));} else {return Number(result);}}/*** 将数值升级(10的X的次⽅)到整数*/function science(num) {var re = {r1: 0, //数字去掉⼩数点后的值,也就是 r1*r2 的结果r2: 1//⼩数部分,10的长度次幂};if (isInteger(num)) { //整数直接返回re.r1 = num;return re;}var snum = scienceNum(num + ""); //处理0.123e-10类似问题var dotPos = snum.indexOf("."); //⼩数点位置var len = snum.substr(dotPos + 1).length; //⼩数点长度re.r2 = Math.pow(10, len);re.r1 = parseInt(snum.replace(".", ""));return re;}/*** 将数值转为字符串** 通过移动⼩数点扩⼤倍数或缩⼩倍数(解决出现e+、e-的问题)** JavaScript在以下情景会⾃动将数值转换为科学计数法:* 1)⼩数点前的数字多于21位。
javascript中数字计算精度缺失问题近⼀段时间有个刚刚⼊⾏开发的朋友问我⼀些计数问题,他说,它命名使⽤ toFixed() ⽅法来截取⼩数的长度了,虽然⼤部分都正常,但是有部分会出现结果不准确的问题。
先看图:例如:结果是:但是:下⾯的就不是想要的结果了。
按道理结果应该是 82.1%的,但是实际上却不是这样。
先不说是什么原因导致的。
然后我让他改⼀下:变成:就改变了⼀下运算位置,但是结果⼤不同。
到底哪⼀个才是正确的呢?我们来借助⼀下计算器:结果已经很明显了,正确答案是 82.21%我朋友他的想法是,先将5755 / 7000 然后得出的结果通过 toFixed() ⽅法来截取3位⼩数,然后再乘以 100 来做⼀个百分百。
按道理来说,在平时的计算中这两个写法都是正确的才对啊,但是为什么会出现这样的情况呢?先不要急,我们先来看⼀个最简单的例⼦:看到这⾥的时候你是不是第⼀时间觉得答案是 0.3 ?那看⼀下js打印的结果是怎么样的。
what?0.1 + 0.2 竟然不是等于 0.3 ?没错,这个就是 js 的计算问题了。
所以上⾯出现答案不正确的问题就是因为js的计算⽅式导致的。
要弄清这个问题,⾸先我们需要了解的是:在计算机中数字是如何存储和运算的。
在计算机中,数字⽆论是整点数还是浮点数都是以多位⼆进制的⽅式进⾏存储的。
在JS中数字采⽤的IEEE 754的双精度标准进⾏存储,我们可以⽆需知道他的存储形式,只需要简单的理解成就是存储⼀个数值所使⽤的⼆进制位数⽐较多⽽已,这样得到的数会更加精确。
这⾥为了简单直观,我们使⽤定点数来说明问题。
在定点数中,如果我们以8位⼆进制来存储数字。
对于整数来说,⼗进制的35会被存储为: 00100011 其代表 2^5 + 2^1 + 2^0。
对于纯⼩数来说,⼗进制的0.375会被存储为: 0.011 其代表 1/2^2 + 1/2^3 = 1/4 + 1/8 = 0.375⽽对于像0.1这样的数值⽤⼆进制表⽰你就会发现⽆法整除,最后算下来会是 0.000110011....由于存储空间有限,最后计算机会舍弃后⾯的数值,所以我们最后就只能得到⼀个近似值。
js精度丢失简单处理
JavaScript中的精度丢失是由于浮点数的表示方式导致的常见
问题。
在JavaScript中,所有数字都以双精度浮点数的形式存储,
这意味着对于一些小数,它们的精度可能会有所丢失。
这种情况可
能会在进行数学运算时出现,特别是涉及到小数点后多位的数字时。
为了简单处理JavaScript中的精度丢失问题,一种常见的方法
是使用数学库中的函数来处理浮点数。
例如,可以使用toFixed()
方法将浮点数转换为指定精度的小数。
这样可以避免由于浮点数表
示方式而导致的精度丢失问题。
另外,可以考虑使用整数运算来避免浮点数运算带来的精度丢失。
将浮点数转换为整数进行运算,然后再将结果转换回浮点数,
可以减少精度丢失的可能性。
此外,可以使用第三方的数学库,如Big.js或Decimal.js来
处理高精度的数学运算。
这些库可以提供更精确的数学运算,避免
了JavaScript原生的浮点数表示方式带来的精度丢失问题。
最后,需要在编写代码时尽量避免比较浮点数的相等性,而是
应该考虑使用范围或误差来进行比较,以减少精度丢失带来的影响。
综上所述,处理JavaScript中的精度丢失问题可以通过使用数
学库函数、整数运算、第三方数学库以及避免直接比较浮点数的相
等性等方法来简单处理。
这些方法可以帮助减少由浮点数表示方式
导致的精度丢失问题,提高数学运算的精确度。
js计算精度不准确的解决方法
在进行js计算时,由于使用的是浮点运算,可能会导致精度问题,从而使计算的结果不准确。
本文将介绍js计算精度不准确的原因,以及可以采取的解决方案。
首先,js计算精度不准确的原因首先是js采用的是浮点运算方式,而不是精确的整数运算方式,这会导致精度问题。
其次,js 内部的运算除法算法有时也会导致精度不准确,因为它无法精确表示一些数字,比如0.3。
在js中,也有一些解决计算精度不准确的方法。
首先,可以利用toFixed()方法,计算结果保留指定的小数位数,从而解决精度问题。
其次,也可以使用Number.EPSILON,将小数转换成整数,从而避免精度问题。
此外,也可以使用toString()方法,将小数转换成字符串,然后再用字符串来进行计算,从而解决精度问题。
虽然js计算精度不准确是因为采用浮点运算,可能会带来一些计算上的不便,但是也有一些解决方案,能够帮助我们解决这个问题,使我们能够在js中进行精确的计算。
总结而言,由于js使用的是浮点运算,可能会导致精度问题,使计算的结果不准确。
但是,也有一些技术手段可以解决这个问题,例如使用toFixed()方法、Number.EPSILON或toString()方法等,使我们可以在js中进行精确的计算。
- 1 -。
关于JS⼩数运算精度问题1.在计算机内部,使⽤⼆进制浮点数并不能准确地表⽰像 0.1, 0.2 或 0.3 这样的数字,所以当编码或解释代码时,像“0.1”其实已经舍⼊为与0.1最接近的数字,即使在计算发⽣之前已经会导致⼩的舍⼊误差(所有语⾔都是这样)3.TS源码/*** @desc 解决浮动运算问题,避免⼩数点后产⽣多位数和计算精度损失。
* 问题⽰例:2.3 + 2.4 = 4.699999999999999,1.0 - 0.9 = 0.09999999999999998*//*** 把错误的数据转正* strip(0.09999999999999998)=0.1*/function strip(num: number, precision = 12): number {return +parseFloat(num.toPrecision(precision));}/*** Return digits length of a number* @param {*number} num Input number*/function digitLength(num: number): number {// Get digit length of econst eSplit = num.toString().split(/[eE]/);const len = (eSplit[0].split('.')[1] || '').length - (+(eSplit[1] || 0));return len > 0 ? len : 0;}/*** 把⼩数转成整数,⽀持科学计数法。
如果是⼩数则放⼤成整数* @param {*number} num 输⼊数*/function float2Fixed(num: number): number {if (num.toString().indexOf('e') === -1) {return Number(num.toString().replace('.', ''));}const dLen = digitLength(num);return dLen > 0 ? strip(num * Math.pow(10, dLen)) : num;}/*** 检测数字是否越界,如果越界给出提⽰* @param {*number} num 输⼊数*/function checkBoundary(num: number) {if (_boundaryCheckingState) {if (num > Number.MAX_SAFE_INTEGER || num < Number.MIN_SAFE_INTEGER) {console.warn(`${num} is beyond boundary when transfer to integer, the results may not be accurate`);}}}/*** 精确乘法*/function times(num1: number, num2: number, ...others: number[]): number {if (others.length > 0) {return times(times(num1, num2), others[0], ...others.slice(1));}const num1Changed = float2Fixed(num1);const num2Changed = float2Fixed(num2);const baseNum = digitLength(num1) + digitLength(num2);const leftValue = num1Changed * num2Changed;checkBoundary(leftValue);return leftValue / Math.pow(10, baseNum);}/*** 精确加法*/function plus(num1: number, num2: number, ...others: number[]): number {if (others.length > 0) {return plus(plus(num1, num2), others[0], ...others.slice(1));}const baseNum = Math.pow(10, Math.max(digitLength(num1), digitLength(num2)));return (times(num1, baseNum) + times(num2, baseNum)) / baseNum;}/*** 精确减法*/function minus(num1: number, num2: number, ...others: number[]): number {if (others.length > 0) {return minus(minus(num1, num2), others[0], ...others.slice(1));}const baseNum = Math.pow(10, Math.max(digitLength(num1), digitLength(num2)));return (times(num1, baseNum) - times(num2, baseNum)) / baseNum;}/*** 精确除法*/function divide(num1: number, num2: number, ...others: number[]): number {if (others.length > 0) {return divide(divide(num1, num2), others[0], ...others.slice(1));}const num1Changed = float2Fixed(num1);const num2Changed = float2Fixed(num2);checkBoundary(num1Changed);checkBoundary(num2Changed);return times((num1Changed / num2Changed), Math.pow(10, digitLength(num2) - digitLength(num1)));}/*** 四舍五⼊*/function round(num: number, ratio: number): number {const base = Math.pow(10, ratio);return divide(Math.round(times(num, base)), base);}let _boundaryCheckingState = true;/*** 是否进⾏边界检查,默认开启* @param flag 标记开关,true 为开启,false 为关闭,默认为 true*/function enableBoundaryChecking(flag = true) {_boundaryCheckingState = flag;}export { strip, plus, minus, times, divide, round, digitLength, float2Fixed, enableBoundaryChecking }; export default { strip, plus, minus, times, divide, round, digitLength, float2Fixed, enableBoundaryChecking };4.当然 npm引⼊最⽅便了,源码还是值得⼀看。
JS数字精度丢失的原因及解决⽅案⽬录前⾔精度丢失原因如何将整数从⼗进制转换为⼆进制将⼩数从⼗进制转换为⼆进制解决⽅案第三⽅库Decimalbignumber变成整数总结前⾔在JavaScript中计算两个⼗进制数的和,有时候会出现令⼈惊讶的结果,相信这个⼤家也都知道了!精度丢失例如,我们在计算0.1 + 0.1得到的结果是0.2,但是计算0.1 + 0.2的结果并不是0.3,⽽是0.30000000000000004这种现象不仅出现在加法,在减法中也会出现类似的结果。
例如1.2 - 1的结果是0.19999999999999996不过这并不是JavaScript独有的,其他编程语⾔也会存在同样的问题。
例如在Python环境下输出。
print(.1 + .2)得到的结果同样是:0.30000000000000004原因这个问题的主要原因是计算机将数据存储为⼆进制。
如何将整数从⼗进制转换为⼆进制⼗进制整数可以通过将其除以 2 转换为⼆进制。
取商,并继续除以 2,直到达到零。
每次执⾏此除法时,请记下余数。
现在反转余数列表,得到⼆进制形式的数字。
举个例⼦,我想把29转成⼆进制:29÷2=14余114÷2=7余07÷2=3余13÷2=1余11÷2=0余1代表⼗进制 29 的⼆进制数是11101.例如:1 也就是110 也就是1010将⼩数从⼗进制转换为⼆进制⼗进制⼩数转换成⼆进制⼩数采⽤"乘2取整,顺序排列"法。
具体做法是:⽤2乘⼗进制⼩数,可以得到积,将积的整数部分取出,再⽤2乘余下的⼩数部分,⼜得到⼀个积,再将积的整数部分取出,如此进⾏,直到积中的⼩数部分为零,此时0或1为⼆进制的最后⼀位。
或者达到所要求的精度为⽌。
举个例⼦:我想把0.375转成⼆进制:0.375*2=0.75 得到00.75*2=1.5得到10.5*2=1 得到1,⼩数没了,结束。
JS⼩数运算失精度的问题浮点数值的最⾼精度是17位⼩数,但在进⾏运算的时候其精确度却远远不如整数;整数在进⾏运算的时候都会转成10进制;⽽java和JavaScript中计算⼩数运算时,都会先将⼗进制的⼩数换算到对应的⼆进制,⼀部分⼩数并不能完整的换算为⼆进制,这⾥就出现了第⼀次的误差。
待⼩数都换算为⼆进制后,再进⾏⼆进制间的运算,得到⼆进制结果。
然后再将⼆进制结果换算为⼗进制,这⾥通常会出现第⼆次的误差。
所以(0.1+0.2)!=03解决这种问题,可以将⼩数变成整数进⾏运算,然后再将结果变为⼩数。
var calc = {/*函数,加法函数,⽤来得到精确的加法结果说明:javascript的加法结果会有误差,在两个浮点数相加的时候会⽐较明显。
这个函数返回较为精确的加法结果。
参数:arg1:第⼀个加数;arg2第⼆个加数;d要保留的⼩数位数(可以不传此参数,如果不传则不处理⼩数位数)调⽤:Calc.Add(arg1,arg2,d)返回值:两数相加的结果*/Add: function (arg1, arg2) {arg1 = arg1.toString(), arg2 = arg2.toString();var arg1Arr = arg1.split("."), arg2Arr = arg2.split("."), d1 = arg1Arr.length == 2 ? arg1Arr[1] : "", d2 = arg2Arr.length == 2 ? arg2Arr[1] : "";var maxLen = Math.max(d1.length, d2.length);var m = Math.pow(10, maxLen);var result = Number(((arg1 * m + arg2 * m) / m).toFixed(maxLen));var d = arguments[2];return typeof d === "number" ? Number((result).toFixed(d)) : result;},/*函数:减法函数,⽤来得到精确的减法结果说明:函数返回较为精确的减法结果。
搞懂js中⼩数运算精度问题原因及解决办法js⼩数运算会出现精度问题js number类型JS 数字类型只有number类型,number类型相当于其他强类型语⾔中的double类型(双精度浮点型),不区分浮点型和整数型。
number类型不同进制number 有四种进制表⽰⽅法,⼗进制,⼆进制,⼋进制和⼗六进制表⽰⽅法⼆进制: 0B或者0b (数字0和字母B或者⼩写字母b) ,后接1或者0表⽰⼆进制数⼋进制: es5下禁⽌表⽰⼋进制数会⾃动转化为⼗进制数,es6⽤0o,后接⼩于8的数字表⽰⼋进制⼗六进制:以0x或者0X开头,后接0-9数字和a-e五个英⽂字母⼗进制:默认直接输⼊0-9都是⼗进制数number进制的转换parseInt和toStringtoString() ⽅法接受⼀个值为 2~36 之间的整数参数指定进制,默认为⼗进制,将Number转为StringparseInt() 第⼆个参数接受⼀个值为 2~36 之间的整数参数指定进制,默认为⼗进制,将String转为Number// toString转换,输⼊为Number,返回为Stringvar n = 120;n.toString(); // "120"n.toString(2); // "1111000"n.toString(8); // "170"n.toString(16); // "78"n.toString(20); // "60"0x11.toString(); // "17"0b111.toString(); // "7"0x11.toString(12);// "15"// parseInt转换,输⼊为String,返回为NumberparseInt('110'); // 110parseInt('110', 2); // 6parseInt('110', 8); // 72parseInt('110', 16); // 272parseInt('110', 26); // 702// toString和parseInt结合使⽤可以在两两进制之间转换// 将 a 从36进制转为12进制var a = 'ra'; // 36进制表⽰的数parseInt(a, 36).toString(12); // "960"OK,扯远了,⼩数,浮点数,及⼩数运算由于Js的所有数字类型都是双精度浮点型(64位)采⽤ IEEE754 标准64位⼆进制数表⽰⼀个number数字其中 64位 = 1位符号位 + 11位指数位 + 52位⼩数位符号位:⽤来表⽰数字的正负,-1^符号位数值,0为正数,1为负数指数位:⼀般都⽤科学计数法表⽰数值⼤⼩,但是这⾥⼀般都是2进制的科学计数法,表⽰2的多少次⽅⼩数位:科学计数法前⾯的数值,IEEE745标准,默认所有的该数值都转为1.xxxxx这种格式,优点是可以省略⼀位⼩数位,可以存储更多的数字内容,缺点是丢失精度⼤概可以理解为这张图浮点数的运算精度丢失问题就是因为,浮点数转化为该标准的⼆进制的过程中出现的丢失整数转⼆进制好理解,除⼆取余法,7表⽰为 111 = 1x2^3 + 1x2^2 + 1x2^1问题来了,⼩数转⼆进制!!由于也需要转化为指数形式,例如 1/2 = 1 * 2^-1, 1/4 = 1 * 2^-2,所以⼩数的转化⼆进制过程是通过判断⼩数是不是满 1/2,1/4,8/1以此类推,换成数学公式就是乘⼆取整法0.1的⼆进制0.1*2=0.2======取出整数部分00.2*2=0.4======取出整数部分00.4*2=0.8======取出整数部分00.8*2=1.6======取出整数部分10.6*2=1.2======取出整数部分10.2*2=0.4======取出整数部分00.4*2=0.8======取出整数部分00.8*2=1.6======取出整数部分10.6*2=1.2======取出整数部分1接下来会⽆限循环0.2*2=0.4======取出整数部分00.4*2=0.8======取出整数部分00.8*2=1.6======取出整数部分10.6*2=1.2======取出整数部分1所以0.1转化成⼆进制是:0.0001 1001 1001 1001…(⽆限循环)0.1 => 0.0001 1001 1001 1001…(⽆限循环)同理0.2的⼆进制是0.0011 0011 0011 0011…(⽆限循环)OK ,转化为⼆进制之后,开始准备运算计算机中的数字都是以⼆进制存储的,⼆进制浮点数表⽰法并不能精确的表⽰类似0.1这样的简单的数字如果要计算 0.1 + 0.2 的结果,计算机会先把 0.1 和 0.2 分别转化成⼆进制,然后相加,最后再把相加得到的结果转为⼗进制但有⼀些浮点数在转化为⼆进制时,会出现⽆限循环。
js数字位数太⼤导致参数精度丢失问题最近遇到个⽐较奇怪的问题,js函数⾥传参,传⼀个位数⽐较⼤,打印arguments可以看到传过来的参数已经改变。
然后查了⼀下,发现确实是js精度丢失造成的。
我的解决⽅法是将数字型改成字符型传输,这样就不会造成精度丢失了。
如下图:JS 数字丢失精度的原因计算机的⼆进制实现和位数限制有些数⽆法有限表⽰。
就像⼀些⽆理数不能有限表⽰,如圆周率 3.1415926...,1.3333... 等。
JS 遵循规范,采⽤双精度存储(double precision),占⽤ 64 bit。
如图意义1位⽤来表⽰符号位11位⽤来表⽰指数52位表⽰尾数浮点数,⽐如1 20.1 >> 0.0001 1001 1001 1001…(1001⽆限循环)0.2 >> 0.0011 0011 0011 0011…(0011⽆限循环)此时只能模仿⼗进制进⾏四舍五⼊了,但是⼆进制只有 0 和 1 两个,于是变为 0 舍 1 ⼊。
这即是计算机中部分浮点数运算时出现误差,丢失精度的根本原因。
⼤整数的精度丢失和浮点数本质上是⼀样的,尾数位最⼤是 52 位,因此 JS 中能精准表⽰的最⼤整数是 Math.pow(2, 53),⼗进制即9007199254740992。
⼤于 9007199254740992 的可能会丢失精度19007199254740992 >> 10000000000000...000 // 共计 53 个 09007199254740992 + 1 >> 10000000000000...001 // 中间 52 个 0239007199254740992 + 2 >> 10000000000000...010 // 中间 51 个 0实际上1 2 3 49007199254740992 + 1 // 丢失9007199254740992 + 2 // 未丢失9007199254740992 + 3 // 丢失9007199254740992 + 4 // 未丢失结果如图以上,可以知道看似有穷的数字, 在计算机的⼆进制表⽰⾥却是⽆穷的,由于存储位数限制因此存在“舍去”,精度丢失就发⽣了。
js做四则运算时,精度丢失问题及解决⽅法⼀、前⾔:这个问题可以说是程序员必踩的坑,因此⽹上针对该问题的分析有很多也很详细,解决⽅法也⽐较统⼀,写法也是⼤同⼩异,本以为预期效果真能如他们所说是完美的,然⽽效果却是差强⼈意。
⼆、问题:⾸先,先来看看两数相加的⼀个经典问题,⽹上找过不少资料的⼈会发现,⼤多数⼈分析精度问题都是由此展开,然⽽最后所谓的解决⽅法成也在它,败也在它。
0.1+0.2=0.30000000000000004⽹上所说的完美解决⽅法: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}应⽤后的输出结果:0.1+0.2=0.3从结果可以看出,这⽅法是完美的解决了这个经典问题,那其他数做运算是不是也都能完美解决呢?在我多数测试后发现并没有,本以为有可能是开发环境或者浏览器导致的问题,但是切换了多个开发环境和浏览器也还是如此,因此可知,⽅法本⾝就有问题。
两数直接相加的输出结果:2.22+2.22=4.444.44+2.22=6.666.66+2.22=8.888.88+2.22=11.10000000000000111.10+2.22=13.3213.32+2.22=15.540000000000001应⽤⽹上找的⽅法的输出结果:2.22+2.22=4.444.44+2.22=6.6600000000000016.66+2.22=8.888.88+2.22=11.10000000000000111.10+2.22=13.3213.32+2.22=15.54对⽐两次结果可以发现,有些数的精度问题是解决了,⽽有些数根本没有解决,甚⾄还导致有些本不会出现精度问题的数产⽣了问题,除了加法,关于减乘除⽹上那些⽅法也⼀概⾏不通,限于篇幅,这⾥就不再⼀⼀举例。
js运算精度丢失问题正常情况下0.0023 * 100 = 0.23,但js运算会出现下⾯这种精度丢失问题:0.0023 - 0.00000011 ==> 0.00229988999999999970.0023 + 0.00000000000001 ==> 0.00230000000000999980.0023 * 100 ==> 0.229999999999999980.0023 / 0.00001 ==> 229.99999999999997实际打印结果:取余也同样存在精度丢失问题:下⾯直接参考别⼈写的⽅法:【中间处理了⼀些问题,⽂章最后有整理优化后的运算⽅法 + 取余的精度⽅法】// 加function floatAdd(arg1, arg2) {var r1, r2, m;try {r1 = arg1.toString().split(".")[1].length;} catch (e) {r1 = 0;}try {r2 = arg2.toString().split(".")[1].length;} catch (e) {r2 = 0;}m = Math.pow(10, Math.max(r1, r2));return (floatMultiply(arg1 , m) + floatMultiply(arg2 , m)) / m;}// 减function floatSub(arg1, arg2) {var r1, r2, m, n;try {r1 = arg1.toString().split(".")[1].length;} catch (e) {r1 = 0;}try {r2 = arg2.toString().split(".")[1].length;} catch (e) {r2 = 0;}m = Math.pow(10, Math.max(r1, r2));// 动态控制精度长度n = (r1 >= r2) ? r1 : r2;return ((floatMultiply(arg1 , m) - floatMultiply(arg2 , m)) / m).toFixed(n);}// 乘function floatMultiply(arg1, arg2) {if(arg1 == null || arg2 == null){return null;}var n1,n2;var r1, r2; // ⼩数位数try {r1 = arg1.toString().split(".")[1].length;} catch (e) {r1 = 0;}try {r2 = arg2.toString().split(".")[1].length;} catch (e) {r2 = 0;}n1 = Number(arg1.toString().replace(".", ""));n2 = Number(arg2.toString().replace(".", ""));return n1 * n2 / Math.pow(10, r1+r2);}// 除function floatDivide(arg1, arg2) {if(arg1 == null){return null;}if(arg2 == null || arg2 == 0){return null;}var n1,n2;var r1, r2; // ⼩数位数try {r1 = arg1.toString().split(".")[1].length;} catch (e) {r1 = 0;}try {r2 = arg2.toString().split(".")[1].length;} catch (e) {r2 = 0;}n1 = Number(arg1.toString().replace(".", ""));n2 = Number(arg2.toString().replace(".", ""));return (n1 / n2) * Math.pow(10, r2 - r1);}上⾯运算⽅法存在的问题:1、floatDivide( 0.0033 , 100 ) = 0.000032999999999999996结果出现精度丢失问题: 解决:除法运算的最后,需要使⽤乘法的⽅法,⽽不能直接相乘:2、floatDivide( 170890000 , 380026238.96999997 ) 得到的结果和实际数出⼊很⼤,如下图: 测试:由上⾯两种情况猜测是乘法⾥⾯的科学计数法的问题: 解决:添加将科学计数法转为字符串的⽅法,得到的结果就正常了,看下图: 下⾯是测试的代码: var toNonExponential = (num)=> {var m = num.toExponential().match(/\d(?:\.(\d*))?e([+-]\d+)/);return num.toFixed(Math.max(0, (m[1] || '').length - m[2]));}/*** 乘法 - js运算精度丢失问题* @param arg1 数1* @param arg2 数2*/var floatMultiply = (arg1, arg2) => {if (arg1 == null || arg2 == null) {return null;}arg1 = toNonExponential(arg1);arg2 = toNonExponential(arg2);var n1, n2;var r1, r2; // ⼩数位数try {r1 = arg1.toString().split(".")[1].length;} catch (e) {r1 = 0;}try {r2 = arg2.toString().split(".")[1].length;} catch (e) {r2 = 0;}n1 = Number(arg1.toString().replace(".", ""));n2 = Number(arg2.toString().replace(".", ""));return n1 * n2 / Math.pow(10, r1 + r2);}var floatDivide = (arg1, arg2) => {if (arg1 == null) {return null;}if (arg2 == null || arg2 == 0) {return null;}var n1, n2;var r1, r2; // ⼩数位数try {r1 = arg1.toString().split(".")[1].length;} catch (e) {r1 = 0;}try {r2 = arg2.toString().split(".")[1].length;} catch (e) {r2 = 0;}n1 = Number(arg1.toString().replace(".", ""));n2 = Number(arg2.toString().replace(".", ""));return floatMultiply((n1 / n2), Math.pow(10, r2 - r1));// return (n1 / n2) * Math.pow(10, r2 - r1); // 直接乘法还是会出现精度问题 }console.log(floatMultiply(4.496794759834739e-9,100000000));// console.log( floatDivide(170890000,380026238.9699997) );// console.log( floatDivide(170890000,380026238.969999) );// console.log( floatDivide(170890000,380026238.96999) );优化后的运算⽅法整合:/*** 将科学计数法的数字转为字符串* 说明:运算精度丢失⽅法中处理数字的时候,如果出现科学计数法,就会导致结果出错 * 4.496794759834739e-9 ==> 0.000000004496794759834739* 4.496794759834739e+9 ==> 4496794759.834739* @param num*/if(num == null) {return num;}if(typeof num == "number") {var m = num.toExponential().match(/\d(?:\.(\d*))?e([+-]\d+)/);return num.toFixed(Math.max(0, (m[1] || '').length - m[2]));}else {return num;}}/*** 乘法 - js运算精度丢失问题* @param arg1 数1* @param arg2 数2* 0.0023 * 100 ==> 0.22999999999999998* {{ 0.0023 | multiply(100) }} ==> 0.23*/var floatMultiply = (arg1, arg2) => {arg1 = Number(arg1);arg2 = Number(arg2);if ((!arg1 && arg1!==0) || (!arg2 && arg2!==0)) {return null;}arg1 = toNonExponential(arg1);arg2 = toNonExponential(arg2);var n1, n2;var r1, r2; // ⼩数位数try {r1 = arg1.toString().split(".")[1].length;} catch (e) {r1 = 0;}try {r2 = arg2.toString().split(".")[1].length;} catch (e) {r2 = 0;}n1 = Number(arg1.toString().replace(".", ""));n2 = Number(arg2.toString().replace(".", ""));return n1 * n2 / Math.pow(10, r1 + r2);}/*** 除法 - js运算精度丢失问题* @param arg1 数1* @param arg2 数2* 0.0023 / 0.00001 ==> 229.99999999999997* {{ 0.0023 | divide(0.00001) }} ==> 230*/var floatDivide = (arg1, arg2) => {arg1 = Number(arg1);arg2 = Number(arg2);if (!arg2) {return null;}if (!arg1 && arg1!==0) {return null;}else if(arg1===0) {return 0;}arg1 = toNonExponential(arg1);arg2 = toNonExponential(arg2);var n1, n2;var r1, r2; // ⼩数位数try {r1 = arg1.toString().split(".")[1].length;} catch (e) {r1 = 0;}try {r2 = arg2.toString().split(".")[1].length;} catch (e) {r2 = 0;}n1 = Number(arg1.toString().replace(".", ""));n2 = Number(arg2.toString().replace(".", ""));return floatMultiply((n1 / n2), Math.pow(10, r2 - r1));// return (n1 / n2) * Math.pow(10, r2 - r1); // 直接乘法还是会出现精度问题}/*** 加法 - js运算精度丢失问题* @param arg1 数1* @param arg2 数2* 0.0023 + 0.00000000000001 ==> 0.0023000000000099998* {{ 0.0023 | plus(0.00000000000001) }} ==> 0.00230000000001*/var floatAdd = (arg1, arg2) => {arg1 = Number(arg1) || 0;arg2 = Number(arg2) || 0;arg1 = toNonExponential(arg1);arg2 = toNonExponential(arg2);var r1, r2, m;try {r1 = arg1.toString().split(".")[1].length;} catch (e) {r1 = 0;}try {r2 = arg2.toString().split(".")[1].length;} catch (e) {r2 = 0;}m = Math.pow(10, Math.max(r1, r2));return (floatMultiply(arg1, m) + floatMultiply(arg2, m)) / m;}/*** 减法 - js运算精度丢失问题* @param arg1 数1* @param arg2 数2* 0.0023 - 0.00000011 ==> 0.0022998899999999997* {{ 0.0023 | minus( 0.00000011 ) }} ==> 0.00229989*/var floatSub = (arg1, arg2) => {arg1 = Number(arg1) || 0;arg2 = Number(arg2) || 0;arg2 = toNonExponential(arg2);var r1, r2, m, n;try {r1 = arg1.toString().split(".")[1].length;} catch (e) {r1 = 0;}try {r2 = arg2.toString().split(".")[1].length;} catch (e) {r2 = 0;}m = Math.pow(10, Math.max(r1, r2));// 动态控制精度长度n = (r1 >= r2) ? r1 : r2;return ((floatMultiply(arg1, m) - floatMultiply(arg2, m)) / m).toFixed(n);}/*** 取余 - js运算精度丢失问题* @param arg1 数1* @param arg2 数2* 12.24 % 12 ==> 0.2400000000000002* {{ 12.24 | mod( -12 ) }} ==> 0.24*/var floatMod = (arg1, arg2) => {arg1 = Number(arg1);arg2 = Number(arg2);if (!arg2) {return null;}if (!arg1 && arg1!==0) {return null;}else if(arg1===0) {return 0;}let intNum = arg1 / arg2;intNum = intNum < 0 ? Math.ceil( arg1 / arg2 ) : Math.floor( arg1 / arg2 ); // -1.02 取整为 -1; 1.02取整为1 let intVal = floatMultiply(intNum, arg2);return floatSub( arg1,intVal ); 12.4}。
关于JavaScript中计算精度丢失的问题摘要:由于计算机是⽤⼆进制来存储和处理数字,不能精确表⽰浮点数,⽽JavaScript中没有相应的封装类来处理浮点数运算,直接计算会导致运算精度丢失。
为了避免产⽣精度差异,把需要计算的数字升级(乘以10的n次幂)成计算机能够精确识别的整数,等计算完毕再降级(除以10的n次幂),这是⼤部分编程语⾔处理精度差异的通⽤⽅法。
关键词:计算精度四舍五⼊四则运算精度丢失1. 疑惑我们知道,⼏乎每种编程语⾔都提供了适合货币计算的类。
例如C#提供了decimal,Java提供了BigDecimal,JavaScript提供了Number……由于之前⽤decimal和BigDecimal⽤得很好,没有产⽣过精度问题,所以⼀直没有怀疑过JavaScript的Number类型,以为可以直接使⽤Number类型进⾏计算。
但是直接使⽤是有问题的。
我们先看看四舍五⼊的如下代码:Js代码1. alert(Number(0.009).toFixed(2));2. alert(Number(162.295).toFixed(2));按正常结果,应该分别弹出0.01和162.30。
但实际测试结果却是在不同浏览器中得到的是不同的结果:在ie6、7、8下得到0.00和162.30,第⼀个数截取不正确;在firefox中得到0.01和162.29,第⼆个数截取不正确;在opera下得到0.01和162.29,第⼆个数截取不正确我们再来看看四则运算的代码:Js代码1. alert(1/3);//弹出: 0.33333333333333332. alert(0.1 + 0.2);//弹出: 0.300000000000000043. alert(-0.09 - 0.01);//弹出: -0.099999999999999994. alert(0.012345 * 0.000001);//弹出: 1.2344999999999999e-85. alert(0.000001 / 0.0001);//弹出: 0.009999999999999998按正常结果,除第⼀⾏外(因为其本⾝就不能除尽),其他都应该要得到精确的结果,从弹出的结果我们却发现不是我们想要的正确结果。