js数值精度

19年目标:消灭英语!我新开了一个公众号记录一个程序员学英语的历程javascript

有提高英语诉求的小伙伴能够关注公众号:csenglish 程序员学英语,天天花10分钟交做业,跟我一块儿学英语吧html


近期在项目中有出现大数值的订单号9148368244236619在调用接口时自动变成9148368244236620的状况,致使请求失误。本文特地总结了出现这种状况的缘由,以及js精度相关的状况。前端

jquery[.data()]方法

在本次案例中,订单号是后端同步渲染到页面上的,java

<div class="j_OrderNumber" data-ordernumber="9148368244236619"></div>
复制代码

呈如今页面上的订单号数值没有问题9148368244236619,前端此时想获取到这个订单号:jquery

var orderNumber = $('.j_OrderNumber).data('ordernumber'); // 9148368244236620 typeof(orderNumber) === 'number' // true 复制代码

在这个取值赋值的过程当中,超过安全值的大数值发生了变化,从9148368244236619变成了91483682442366120,这里的问题主要出如今jquerydata方法。程序员

超过安全值Math.pow(2, 53)-1 -- 9007199254740991 的number类型在赋值的过程当中会发生精度丢失,而咱们在使用jquery的data方法取得的自定义html属性data-ordernumber值会强制转换成number类型,致使精度丢失。后端

而使用js原生的方法取dom节点的属性时,获取到的值都是string类型,这样就不会出现number类型精度丢失的问题。安全

var orderNumber = $('.j_OrderNumber')[0].dataset.ordernumber // '9148368244236619'

typeof(orderNumber) === 'string' // true

复制代码

因此咱们在经过自定义属性取值number类型时,而且预期这些值会是相似订单号这种会超过安全阈值的数值时,不要使用jquery的data方法。bash

大数值的精度问题

可以被“安全”呈现的最大整数是Math.pow(2, 53) - 1,即9007199254740992。在ES6中被定义为Number.MAX_SAFE_INTEGERdom

在开发环境,根据程序的特殊性,在有可能出现这种状况时咱们应该杜绝掉超出安全阈值的大整数,并给出友好提示:

function isSafeInteger(num) {
	return typeof(num) === 'number' && num % 1 == 0 && Math.abs(num) <= Math.pow(2, 53) -1;
}
复制代码

在通常电商业务中比较常见出现大数值的场景也就是订单号了,这类场景后端传值给咱们的时候都强制包装成string类型就会解决大多数的精度丢失问题了。

至于为何会只有2的53次方-1的整数是安全的,能够看阮神的关于数值的文章有详细介绍

小数的精度问题

经典的 0.1 + 0.2 === 0.3 // false 问题

0.1 + 0.2 === 0.30000000000000004

小数比较

对于这类数值比较问题,若是咱们已经知道了目标比较值,即若是咱们已经明确要与0.3进行比较,咱们也能够不须要获得0.1+0.2的真实指望结果值(0.3),由于若是咱们要获得0.3,还须要对0.1和0.2进行操做。常规解法:

(0.1 * 10 + 0.2 * 10) / 10 = 0.3
复制代码

在「你不知道的javascript」一书中有提到一种判断方法,设置一个偏差范围值,一般也称为“机器精度”,对于javascript来讲,这个值一般为Math.pow(2, -53)

咱们能够将须要比较的两个值进行相减,再与这个机器精度进行比较,若是在偏差范围内,咱们也视为两个值是相等的。

function numbersCloseEnoughToEqual(num1, num2) {
	return Math.abs(num1 - num2) < Math.pow(2, -53);
}
复制代码

小数展现

对于电商业务来说,小数通过四则运算后可能会出现失去精度的问题,可是做为展现来讲咱们都会调用toFixed()进行小数后几位的约定,调用了这个方法后小数失去精度的问题也就迎刃而解了,不可能出现0.30000000000000004这样的数值。

因此在业务中有须要进行小数四则运算并会展现在页面中,调用toFixed()方法!

可是toFixed()也有失去精度的时候!

1.335.toFixed(2)
// "1.33"
复制代码

解决办法

function toFixed(num, s) {
    var times = Math.pow(10, s)
    var des = num * times + 0.5
    des = parseInt(des, 10) / times
    return des + ''
}
复制代码

小数的四则运算

参考

本文来自二口南洋,有什么须要讨论的欢迎找我。

相关文章
相关标签/搜索