Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

如何解决0.1 +0.2===0.30000000000000004类问题 #11

Open
FrankKai opened this issue Mar 2, 2018 · 0 comments
Open

如何解决0.1 +0.2===0.30000000000000004类问题 #11

FrankKai opened this issue Mar 2, 2018 · 0 comments

Comments

@FrankKai
Copy link
Owner

FrankKai commented Mar 2, 2018

上篇博客深度剖析了0.1+0.2 === 0.30000000000000004的原因。
这篇博客将主要提供几种解决小数精度丢失问题的Javascript类库的代码示例,以及简单的原生EcmaScript方法的代码示例。
###一.类库部分

math.js

math.js是JavaScript和Node.js的一个广泛的数学库。支持数字,大数,复数,分数,单位和矩阵等数据类型的运算。

官网:http://mathjs.org/
GitHub:https://github.com/josdejong/mathjs

0.1+0.2 ===0.3实现代码:

var math = require('mathjs')
console.log(math.add(0.1,0.2))//0.30000000000000004
console.log(math.format((math.add(math.bignumber(0.1),math.bignumber(0.2)))))//'0.3'

decimal.js

为 JavaScript 提供十进制类型的任意精度数值。

官网:http://mikemcl.github.io/decimal.js/

GitHub:https://github.com/MikeMcl/decimal.js

var Decimal = require('decimal.js')
x = new  Decimal(0.1)
y = 0.2
console.log(x.plus(y).toString())//'0.3'

bignumber.js

用于任意精度算术的JavaScript库。

官网:http://mikemcl.github.io/bignumber.js/

Github:https://github.com/MikeMcl/bignumber.js

var BigNumber = require("bignumber.js")
x = new BigNumber(0.1)
y = 0.2
console.log(x.plus(y).toString())//'0.3'

big.js

用于任意精度十进制算术的小型快速JavaScript库。
官网:http://mikemcl.github.io/big.js/
Github:https://github.com/MikeMcl/big.js/

var Big = require("big.js")
x = new Big(0.1)
y = 0.2
console.log(x.plus(y).toString())//'0.3'

有一个需要注意的点,使用类库此时输出的0.3是String类型,因此若想保持为Number类型,可使用parseFloat()方法。

还有一个注意点,在本地install测试的时候,npm i mathjs -g ,require是也要require('mathjs'),而不是带点的math.js,因为josdejong这哥们在创建项目的时候就命名为mathjs,而同时拥有上述decimal.js, bignumber.js和big.js的MikeMcl,项目名字就带了dot,因此安装和引入时,都是xxx.js的形式。

如何在这三个类库之间做选择,还需要大家自己根据具体情况具体分析,我在这里就不赘述了。

最后,教大家一个线上直接测试的网站,https://npm.runkit.com,子路径输入想要测试的Node.js package名,就可以实现在线测试包中的api了。
例如:
math.js:https://npm.runkit.com/mathjs
big.js:https://npm.runkit.com/big.js
###二、原生方法
类库其实很强大,我们计算0.1+0.2其实只是用到了冰山一角,那么我们如何使用原生的EcmaScript代码来应用于简单的问题场景呢?

这就要用到Number.prototype.toFixed()这个方法了。

浮点数运算

toFixed() 方法

浮点数运算的解决方案有很多,这里给出一种目前常用的解决方案, 在判断浮点数运算结果前对计算结果进行精度缩小,因为在精度缩小的过程总会自动四舍五入。

toFixed() 方法使用定点表示法来格式化一个数,会对结果进行四舍五入。语法为:

JavaScript 代码:
numObj.toFixed(digits)
参数 digits 表示小数点后数字的个数;介于 0 到 20 (包括)之间,实现环境可能支持更大范围。如果忽略该参数,则默认为 0。

返回一个数值的字符串表现形式,不使用指数记数法,而是在小数点后有 digits 位数字。该数值在必要时进行四舍五入,另外在必要时会用 0 来填充小数部分,以便小数部分有指定的位数。 如果数值大于 1e+21,该方法会简单调用 Number.prototype.toString()并返回一个指数记数法格式的字符串。

特别注意:toFixed() 返回一个数值的字符串表现形式。

具体可以查看 MDN中的说明,那么我们可以这样解决精度问题:

JavaScript 代码:

parseFloat((数学表达式).toFixed(digits)); // toFixed() 精度参数须在 0 与20 之间
// 运行
parseFloat((0.1 + 0.2).toFixed(10))//结果为0.3
parseFloat((0.3 / 0.1).toFixed(10)) // 结果为 3  
parseFloat((0.7 * 180).toFixed(10))//结果为126
parseFloat((1.0 - 0.9).toFixed(10)) // 结果为 0.1   
parseFloat((9.7 * 100).toFixed(10)) // 结果为 970 
parseFloat((2.22 + 0.1).toFixed(10)) // 结果为 2.32

在Browser环境精度参数允许0100位之间(包括100),测试版本为Chrome62(64位)和Firefox56 (32 位)。
在Nodejs环境中,只能是0
20之间,测试版本为v6.9.5。

其次就是toFixed()的浏览器兼容性讨论,MDN给出的结果全部是YES,无论desktop端还是mobile端,也就是说不用担心toFixed()的兼容性问题(ie8- 我们不做讨论)。

desktop端:

mobile端:

Thanks:
http://www.css88.com/archives/7340#more-7340
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Number/toFixed

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant