轶哥

妄图改变世界的全栈程序员。

JavaScript 安全计算
  •   更新:2020-09-17 18:02:17
  •   首发:2020-08-07 19:26:16
  •   教程
  •   1830

C/C++、Java、JavaScript等语言,都有计算不精准的问题。原因是这些语言在计算的时候遵循IEEE 二进制浮点数算术标准(IEEE 754),跟CPU浮点运算器有关系。

0.1+0.2!=0.3

“为什么JS中计算0.1加0.2的时候不等于0.3”,“为什么JS中计算4.44乘以100不等于444”,这类问题在诸多程序员岗位的面试中很常见。各个语言也提供了相应的数学库解决计算精度的问题。

在涉及大量计算的JavaScript项目中,强烈推荐使用Math.js这个精确计算库解决JS计算不精准的问题。

Math.js是一个用于JavaScript和Node.js的高级数学计算函数库。它内置大量的函数和常量,具有支持符号计算的表达式解析器,并提供了一个集成的解决方案用于处理不同的数据类型,例如numbers、 big numbers、 complex numbers、 fractions、 units和matrices。强大且易于使用。

特征

  • 支持数字,大数,复数,分数,单位,字符串,数组和矩阵。
  • 与JavaScript的内置Math库兼容。
  • 包含一个灵活的表达式解析器。
  • 进行符号计算。
  • 带有大量内置函数和常量。
  • 也可以用作命令行应用程序。
  • 在任何JavaScript引擎上运行。
  • 很容易扩展。
  • 开源。

安装


npm install mathjs

更多内容请直接访问Math.js的开源仓库:https://github.com/josdejong/mathjs

示例

常规计算(加减乘除)


import { create, all } from 'mathjs'



const config = {

  number: 'BigNumber',

  precision: 64

}



const math = create(all, config)



math.evaluate('4.44 * 100')

math.evaluate('0.1 + 0.2')

JS%E7%B2%BE%E5%87%86%E8%AE%A1%E7%AE%97.png

四舍五入

在JavaScript中toFixed()方法可把 Number 四舍五入为指定小数位数的数字。

NumberObject.toFixed(num)

需要注意的是,toFixed()同样存在精度问题。例如2.55取一位小数四舍五入的结果可能是2.5

toFixed.png

在JS自带的函数Math.round()中,3.5将舍入为 4,而 -3.5将舍入为-3

为了避免这类问题,可以通过mathjs提供的round()方法。

Round

将值四舍五入到最接近的整数。对于矩阵,该函数按元素进行求值。

句法

math.round(x)
math.round(x, n)

参数

参数 类型 描述
x number| BigNumber | Fraction | Complex | Array | Matrix 四舍五入
n number| BigNumber | Array 小数位数默认值:0。

返回值

类型 描述
number| BigNumber | Fraction | Complex | Array | Matrix 取整值

例子

math.round(3.2)              // returns number 3
math.round(3.8)              // returns number 4
math.round(-4.2)             // returns number -4
math.round(-4.7)             // returns number -5
math.round(math.pi, 3)       // returns number 3.142
math.round(123.45678, 2)     // returns number 123.46

const c = math.complex(3.2, -2.7)
math.round(c)                // returns Complex 3 - 3i

math.round([3.2, 3.8, -4.7]) // returns Array [3, 4, -5]

mathjs.png

math.js更多方法:https://mathjs.org/docs/reference/functions.html

取整

在JavaScript中,parseInt()方法可以实现取整。

parseInt(string, radix)

第二个参数radix可选。表示要解析的数字的基数。

同样的,通过parseInt()方法取整也存在精度问题。例如:

parseInt(0.0000001)的结果是1

parseInt.png

解决这个问题,只需要确保传入的第一个参数是字符串即可,例如:parseInt('0.0000001', 10),得到的结果就是0了(**注意:parseInt('1e-7')的结果也是1**)。

第二个参数最好明确指明数字是10进制,否则可能因浏览器遵循的ECMA标准而存在返回值差异。

当参数 radix 的值为 0,或没有设置该参数时,parseInt() 会根据 string 来判断数字的基数。

  • 如果 string 以 "0x" 开头,parseInt() 会把 string 的其余部分解析为十六进制的整数。
  • 如果 string 以 0 开头,那么 ECMAScript v3 允许 parseInt() 的一个实现把其后的字符解析为八进制或十六进制的数字。
  • 如果 string 以 1 ~ 9 的数字开头,parseInt() 将把它解析为十进制的整数。

可这带来一个新的问题,parseInt(0.0000001)产生了隐式转换,得到的结果不是我们想要的。如何将0.0000001正确的转换为'0.0000001'呢?

对于非负小数取整,我们可以采用Math.floor(0.0000001)方法实现向下取整,避免转换过程。

如果是负数,那么Math.floor(-0.0000001)将会得到-1,而parseInt('-0.0000001')得到的结果是-0

因此,对于不超过4294967295(有效整数,int,范围是0-0xffffffff)的数字来说,JavaScript向下取整,可以采用-0.0000001 | 0,或者-0.0000001 >>> 0来实现。

>>>是无符号位移。所以>>> 0 在JS里面是确保数字有意义的判断方法。

安全值与最大数

1.7976931348623157e+308,这个数字究竟意味着什么?

maxvalue.jpg

Number.MAX_SAFE_INTEGER 是js里整数的安全的最大值,由于 js 用的是 IEEE 754 双精度浮点,可以安全地表示 [ -2^53+1 , 2^53-1 ] 这个范围。

Number.MAX_VALUE 属性是 JavaScript 中可表示的最大的数。

打赏
交流区

暂无内容

尚未登陆
发布
  上一篇 (PHP 获取客户端真实IP地址)
下一篇 (路由器当交换机/AP用)  

评论回复提醒