1. js 双精度浮点数
一、怎样将一个数据转成浮点数 https://www.hu.com/question/21711083
二、js 的 Number
在 javaScript 中整数和浮点数都属于 Number 数据类型,所有数字都是以 64 位浮点数形式储存,即便整数也是如此。
三、造成哪些问题?
1、小数计算精度丢失,比如 0.1+0.2 不等于 0.3
2、整数最大范围
整数是按最大54位来算最大(253 - 1,Number.MAX_SAFE_INTEGER,9007199254740991) 和最小(-(253 - 1),Number.MIN_SAFE_INTEGER,-9007199254740991) 安全整数范围的。所以只要超过这个范围,就会存在被舍去的精度问题。
四、解决办法
开源的库、bigInt、
0.1+0.2-0.3 // 5.551115123125783e-17
5.551115123125783e-17.toFixed(20) //脊颤 '0.00000000000000005551'
5.551115123125783e-17<Number.EPSILON*Math.pow(2,2) // true
重新整理
https://zhuanlan.hu.com/p/73699947
回顾一个基础问题,js 中的精度丢失问题。
一、在 js 中只有双精度浮点数来存储的Number,数据存储会有三个步骤:1、十进制转二进制 2、二进制转科学技术法 3、按 IEEE754 标准存储。
二、双精度浮点一共有 64位,64位比特又可分为三个部分:森轮
符号位S:第 1 位是正负数符号位(sign),0代表正数,1代表负数
指数位E:中间的 11 位存储指数(exponent),用来表示次方数
尾数位M:最后的 52 位是尾数(mantissa),超出的部分自动进一樱春败舍零
三、基于以上知识,在数据小数位在进行转换二进制时,会出现无线循环的情况,而数据转成 IEEE754标准时又仅支持 52 位,所以要发生一个数据截断,也就是精度丢失。
四、常见的丢失场景,
0.1 + 0.2 === 0.30000000000000004
parseInt(0.58*100,10)=57
(1.335).toFixed(2)
四、解决办法
math.js
bignumber.js
等库以及 es6 针对整数精度丢失的新数据类型BigInt
2. js浮点数精度误差问题,解决方法
JavaScript 是一门弱类型的语言,从设计思想上就没有对浮点数有个严格的数据类型,所以精度误差的问题就显得格外突出。下面就分析下为什么会有这个精度误差,以及怎样修复这个误差。
首先,我升坦们要站在计算机的角度思考 0.1 + 0.2 这个看似小儿科的问题。我们知道,能被计算机读懂的是二进制,而不是十进制,所以我凳笑毕们先把 0.1 和 0.2 转换成二进制看看:
0.1 => 0.0001 1001 1001 1001…(无限循环)
0.2 => 0.0011 0011 0011 0011…(无限循环)
双精度浮点数的小数部分最多支持 52 位,所以两者相加之后得到这么一串 0. 因浮点数小数位的限制而截断的二进制数字,这时候,我们再把它转换为十进制,就成了 0.30000000000000004。
原来如此,那怎么解决这个问题呢?我想要的结果就是 0.1 + 0.2 === 0.3 啊!!!
有种最简单的解决方案,就是给出明确的精度要求,在返回值的过程中,计算机会自动四舍五入,比如:
var numA = 0.1;
var numB = 0.2;
alert( parseFloat((numA + numB).toFixed(2)) === 0.3 );
乘法运算中有这枣芹种,比如0.58*100,结果是57.99999999999999。可以用Math.round()进行处理,
------||-------
尽量避免对小数进行操作,先处理成整数后在进行操作,其结果会比较精确。
3. js小数加减为什么会失精
JavaScript的数字使用的是浮点数,浮点数可以表示的数的个数有限(只有很少一部分数可以表示),其他的数在浮点书里面不存在
其他语言同样有这个问题,只是在语言层面做了处理,开发者可能不需要考虑精度问题,但JavaScript语言并没有对结果进行底层处理,所以就会有问题
提示:js的加减乘除等计算都有问题
4. JS位运算异常(位运算精度丢失)的原因探究
《【转+补充】深入研究js中的位运算及用法》
《【JS时间戳】获取时间戳的最快方式探究》
日常开发中一直没遇到过位运算导致精度丢失的问题,直到这天,搞10位时间戳取整的时候,终于被我撞上了。具体是个什么场景呢,我们来还原下案发现场:
可以看到输出的结果为:
得到的 t 是一个精确到微秒的时间戳。但是请求接口的时候需要的是一个10位(精确到秒)的时间戳,所以这里需要将它转换为10位,自然就是 ➗1000 即可,然后通过位运算来实现类似 Math.trunc 的取证效果,得到了我们要的10位时间戳。至此完美解决!那问题又是如何发生的呢?
按照上面的运算规律,如果我们要获取13位时间戳,是不是直接对 t>>0 就可以了呢?我们来看一下:
输出结果如下:
WTF!!!看到了咩!!!居然输出了一个负数!!!我们想要的结果应该段型是 1597113682985 才对啊!为什么会出现了负数呢!!!
由此,怪物出现啦!我们今天就来解读(xiang fu)一下它!
想到这里,我们一定就会怪是位运算的锅!那这个锅该怎么让位运算背起来呢!我们来研究一下!
首先我们知道,JS中没有真正的整型,数据都是以double(64bit)的标准格式存储的,这里就不再赘述了,要想搞透其中的原理,请打开 【传送门】
位运算是在数字底层(即表示数字的 32 个数位)进行运算的。由于位运算是低级的运算操作,所以速度往往也是最快的(相对其它运算如加减乘除来说),并且借助位运算有时我们还能实现更简单的程序逻辑,缺点是很不直观,许毁伍多场合不能够使用。
以下来源于w3shool:
ECMAScript 整数有两种类型,即有符号整数(允许用正数和负数)和无符号整数(只允许用正数)。 在 ECMAScript 中,所有整数字面量默认都是有符号整数 ,这意味着什么呢?
有符号整数使用 31 位表示整数的数值,用第 32 位表示整数的符号,0 表示正数,1 表示负数。数值范围从 -2147483648 到 2147483647 。
可以以两种不同的方式存储二进制形式的有符号整数,一种用于存储正数,一种用于存储负数。 正数是以真二进制形式存储的 ,前 31 位中的每一位都表示 2 的幂,从第 1 位(位 0)开始,表示 20,第 2 位(位 1)表示 21。没用到的位用 0 填充,即忽略不计。例如,下图展示的是数 18 的表示法。
那在js中二进制和十进制如何转换呢?如下
负数同样以二进制存储,但使用的格式是二进制补码。计算一个数值的二进制补码,需要经过下列3个步骤:
例如,要确握余猜定-18的二进制表示,首先必须得到18的二进制表示,如下所示:
0000 0000 0000 0000 0000 0000 0001 0010
接下来,计算二进制反码,如下所示:
1111 1111 1111 1111 1111 1111 1110 1101
最后,在二进制反码上加 1,如下所示:
1111 1111 1111 1111 1111 1111 1110 1101 +
0000000000000000000000000000 0001 =
1111 1111 1111 1111 1111 1111 1110 1110
因此,-18 的二进制就是 1111 1111 1111 1111 1111 1111 1110 1110
而其相反数18的二进制为 0000 0000 0000 0000 0000 0000 0001 0010
ECMAScript会尽力向我们隐藏所有这些信息,在以二进制字符串形式输出一个负数时,我们看到的只是这个负数绝对值的二进制码前面加上了一个负号
JavaScript 只有一种数字类型 ( Number )
我们将 1596596596.3742654.toString(2) 转为二进制字符串表示如下:
.0101111111001111110111
但实际在内存中的存储如下:
说到这里就不得不简单提一下数字精度丢失的问题。上面也知道,JS中所有的数字都是用double方式进行存储的,所以必然会存在精度丢失问题。
以下转自文章: JavaScript数字精度丢失问题总结
此时只能模仿十进制进行四舍五入了,但是二进制只有 0 和 1 两个,于是变为 0 舍 1 入。这即是计算机中部分浮点数运算时出现误差,丢失精度的根本原因。
大整数的精度丢失和浮点数本质上是一样的,尾数位最大是 52 位,因此 JS 中能精准表示的最大整数是 Math.pow(2, 53) ,十进制即 9007199254740992
大于 9007199254740992 的可能会丢失精度:
9007199254740992 >> 10000000000000...000 ``// 共计 53 个 0
9007199254740992 + 1 >> 10000000000000...001 ``// 中间 52 个 0
9007199254740992 + 2 >> 10000000000000...010 ``// 中间 51 个 0
实际上
9007199254740992 + 1 ``// 丢失
9007199254740992 + 2 ``// 未丢失
9007199254740992 + 3 ``// 丢失
9007199254740992 + 4 ``// 未丢失
以上,可以知道看似有穷的数字, 在计算机的二进制表示里却是无穷的,由于存储位数限制因此存在“舍去”,精度丢失就发生了。
想了解更深入的分析可以看这篇论文(你品!你细品!): What Every Computer Scientist Should Know About Floating-Point Arithmetic
关于精度和范围的内容可查看 【JS的数值精度和数值范围】
通过前面的知识补充,我们已经知道:
这也就是为什么对于整数部位为10位的时间戳,通过位运算可以进行取整(因为目前时间戳159xxxxxxx<2147483647),不存在时间戳超过范围的问题。但是对于13位时间戳,如 1596615447123>2147483647 ,此时再通过位运算操作的时候就会导致异常,如:
这主要是因为在进行位运算之前,JS会先将64bit的浮点数 1596615447015.01 转为32bit的有符号整型后进行运算的,这个转换过程如下:
为了验证上述过程,我们再举一个例子: 1590015447015.123 >> 0 = 877547495
将将将将!没错的吧!所以JS的这个坑还真是。。。 让人Orz
5. java 浮点数为什么精度会丢失
并不是java的浮点数精度会丢失,而是所有用二进制存储中的浮点数都可能会精度丢失(部分特殊的小数数值可以精确表示),所以计算机中存储的浮点数都存在精度丢失的风险,不过一边这个丢失的精度对我们正常的使用不会构成影响。
小数在转换为二进制时并不一定能用一个精确的二进制表示,大多数时候都是取的一个近似值,这就造成了精度的丢失。如果再用这个二进制进行计算,明显计算结果的精度会进一步丢失。
举个简单的例子把0.1用二进制表示(小数与二进制转换方法)
(1)0.1x2=0.2取整数位0得0.0
(2)0.2x2=0.4取整数位0得0.00
(3)0.4x2=0.8取整数位0得0.000
(4)0.8x2=1.6取整数位1得0.0001
(5)0.6x2=0.2取整数位1得0.00011
(6)0.2x2=0.4取整数位0得0.000110
(7)0.4x2=0.8取整数位0得0.0001100
(8)0.8x2=1.6取整数位1得0.00011001
(9)0.6x2=1.2取整数位1得0.000110011
(n)...
得到一个无限循环的二进制小数 0.000110011…,没办法用一个精确的二进制表示0.1。而且计算机中存储一个浮点数所用的位数也是有限的,所以只能选择在某一个精度进行保存。
当然也有特殊的小数,比如0.25的二进制为0.01
附:代码之谜(五)- 浮点数(谁偷了你的精度?)
6. JavaScript为什么浮点数会丢失精度
JS浮点计算问题
问题
用js进行浮点数计算,结果可能会“超出预期”,大部分计算结果还是对的,但是我们可不想在计算这么严谨的事情上还有意外的惊喜。比如:
0.3 + 0.6 = 0.8999999999999999
0.3 - 0.2 = 0.09999999999999998
0.3 * 1.5 = 0.44999999999999996
0.3 / 0.1 = 2.9999999999999996
看完这几个计算结果,如果你没用过js,你可能会有点崩溃。我只能说,这就是js的魅力所在。
分析
在这之前,你需要知道以下几点:
js中数字类型只有Number;
js的Number是IEEE 754标准的64-bits的双精度数值
网上有很多关于此问题的解释,由于计算机是用二进制来存储和处理数字,不能精确表示浮点数,而js中没有相应的封装类来处理浮点数运算,直接计算会导致运算精度丢失。其实高级语言(c#,java)也存在此问题,只不过它们自己内部做了处理,把这种精度差异给屏蔽掉了。有些小数转换为二进制位数是无穷的(有循环),但是64位中小数最多只有52位,因此对于位数超过的相当于被截取了,导致了精度的丢失。这个地址可以用来浮点数和IEEE 754标准的64-bits的互转(背后是二进制的转换),用这个我们来验证下0.3-0.2。
0.3转换后为0.
0.2转换后为0.
0.-0.=0.
这和js直接计算的结果0.09999999999999998想吻合。
分析下来,终于明白并不是js自身发育不良,只是没有及时补充营养,我们只能另想出路了。
以上是网上找的
我以前遇到过问题2中浏览器计算的结果 是两种,所以和浏览器也有问题
7. js哪些运算精度丢失
由于在H5页面上需要进行动态的金额计算,且金额涉及到了小数,因而随之产生了浮点数计算的精度丢失问题。
刚开始的时候,测试给提了一个金额计算误差的问题,刚开始我还没怎么重视,然后瞅了瞅代码,随便改了改做了些异常处理,然后就给提交了。
接着,测试又提了一个bug,“6.8-0.9=5.8”。然后顿时我就蒙逼了,随后突然意识到,JS作为解释性语言,直接计算会有浮点数精度丢失问题。接下来,在网上找了一些资料,然后也根据具体的原理自己做了一些修改,最终解决了问题。
浮点数的二进制表示:
IEEE 754 标准是IEEE二进位浮点数算术标准(IEEE Standard for Floating-Point Arithmetic)的标准编号,等同于国际标准ISO/IEC/IEEE 60559。该标准由美国电气电子工程师学会(IEEE)计算机学会旗下的微处理器标准委员会(Microprocessor Standards Committee, MSC)发布。这个标准定义了表示浮点数的格式(包括负零-0)与反常值(denormal number),一些特殊数值(无穷(Inf)与非数值(NaN)),以及这些数值的「浮点数运算子」;它也指明了四种数值修约规则和五种例外状况(包括例外发生的时机与处理方式)。
JS的浮点数实现也是遵循IEEE 754标准,采用双精度存储(double precision),进行了相关的实现。其中1位用来表示符号位,11位用来表示指数,52位表示尾数。
解决方案:
本质上在处理这类问题的时候,基本的思路就是通过将浮点数转换成整数进行计算,然后再将整数的小数点位调整,转回正确的浮点数结果。
8. js javascript 四舍五入结果不正确 不对
我测试了你的表达式,结果是3488.4849999999997。四舍五入为3488.48。
javascript做浮点数运算时,由于浮回点数精度的问题,会造成答运算结果不准确。
解决办法是:将小数运算转化为整数运算,这样就不会丢失精度。
functionmultiply(arg1,arg2)
{
varm=0,s1=arg1.toString(),s2=arg2.toString();
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)
}
multiply(500.50,6.97);
/*
3488.485
*/
既想保证精度,又想简单,那没有这样的方法。其实这个方法本身并不复杂。
求网友推荐~
9. javascript怎么解决浮点数计算不精确的问题
Math.add=function(v1,v2)
{
///<summary>精确计算加法。语法:Math.add(v1,v2)</summary>
///<paramname="v1"type="number">操作数。</param>
///<paramname="v2"type="number">操作数。</param>
///<returnstype="number">计算结果。</returns>
varr1,r2,m;
try
{
=v1.toString().split(".")[1].length;
}
catch(e)
{
r1=0;
}
try
{
r2=v2.toString().split(".")[1].length;
}
catch(e)
{
r2=0;
}
m=Math.pow(10,Math.max(r1,r2));
return(v1*m+v2*m)/m;
}
Number.prototype.add=function(v)
{
///<summary>精确计算加法。语法:number1.add(v)</summary>
///<paramname="v"type="number">操作数。</param>
///<returnstype="number">计算结果。</returns>
returnMath.add(v,this);
}
Math.sub=function(v1,v2)
{
///<summary>精确计算减法。语法:Math.sub(v1,v2)</summary>
///<paramname="v1"type="number">操作数。</param>
///<paramname="v2"type="number">操作数。</param>
///<returnstype="number">计算结果。</returns>
returnMath.add(v1,-v2);
}
Number.prototype.sub=function(v)
{
///<summary>精确计算减法。语法:number1.sub(v)</summary>
///<paramname="v"type="number">操作数。</param>
///<returnstype="number">计算结果。</returns>
returnMath.sub(this,v);
}
Math.mul=function(v1,v2)
{
///<summary>精确计算乘法。语法:Math.mul(v1,v2)</summary>
///<paramname="v1"type="number">操作数。</param>
///<paramname="v2"type="number">操作数。</param>
///<returnstype="number">计算结果。</returns>
varm=0;
vars1=v1.toString();
vars2=v2.toString();
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);
}
Number.prototype.mul=function(v)
{
///<summary>精确计算乘法。语法:number1.mul(v)</summary>
///<paramname="v"type="number">操作数。</param>
///<returnstype="number">计算结果。</returns>
returnMath.mul(v,this);
}
Math.div=function(v1,v2)
{
///<summary>精确计算除法。语法:Math.div(v1,v2)</summary>
///<paramname="v1"type="number">操作数。</param>
///<paramname="v2"type="number">操作数。</param>
///<returnstype="number">计算结果。</returns>
vart1=0;
vart2=0;
varr1,r2;
try
{
t1=v1.toString().split(".")[1].length;
}
catch(e)
{
}
try
{
t2=v2.toString().split(".")[1].length;
}
catch(e)
{
}
with(Math)
{
r1=Number(v1.toString().replace(".",""));
r2=Number(v2.toString().replace(".",""));
return(r1/r2)*pow(10,t2-t1);
}
}
Number.prototype.div=function(v)
{
///<summary>精确计算除法。语法:number1.div(v)</summary>
///<paramname="v"type="number">操作数。</param>
///<returnstype="number">计算结果。</returns>
returnMath.div(this,v);
}
10. 关于js中小数运算丢失精度的处理办法
浮点数值的最高精度是17位小数,但在进行运算的毁基时候其精确度却远远不如整数;整数在进行运算的时候都会转成10进制; 而java和JavaScript中计算小数运算时,都会先将十进制的小数换算到对应的二进制,一部分散高小数并不能完整的换算为二进制,这里就出现了第一次的误差。待小数都换算为二进制后,再进行二进制间的运算,得到二进制结果。然后再将二进制结果换算为十进制,这里通常会出现第二次的误差。
所以(0.1+0.2)!=03
解决这种问题,可以将小数变成整数进行运算,然后再将结果变为小数。
//乘法
function multiNum (a,b){
var c = 0,
d = a.toString(),
e = b.toString();
try {
c += d.split(".")[1].length;
} catch (f) { }
try {
c += e.split(".")[1].length;
} catch (f) { }
return Number(d.replace(".","")) * Number(e.replace(".","")) / Math.pow(10,c);
}
//除法
function divide (a,b){
var c,d,e = 0,
f = 0;
try {
e = a.toString().split(".")[1].length;
} catch (g) { }
try {
冲余尺 f = b.toString().split(".")[1].length;
} catch (g) { }
return c = Number(a.toString().replace(".","")),d = Number(b.toString().replace(".","")),this.mul(c / d,Math.pow(10,f - e));
}
//加法
function addNum (a,b){
var c,d,e;
try {
c = a.toString().split(".")[1].length;
} catch (f) {
c = 0;
}
try {
d = b.toString().split(".")[1].length;
} catch (f) {
d = 0;
}
return e = Math.pow(10,Math.max(c,d)),(multiNum(a,e) + multiNum(b,e)) / e;
}
//减法
function subNum (a,b) {
var c,d,e;
try {
c = a.toString().split(".")[1].length;
} catch (f) {
c = 0;
}
try {
d = b.toString().split(".")[1].length;
} catch (f) {
d = 0;
}
return e = Math.pow(10,Math.max(c,d)),(multiNum(a,e) - multiNum(b,e)) / e;
}