Personal Blog

轶哥

轶哥的头像

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

Article

JavaScript 安全计算

目录
  1. 特征
  2. 安装
  3. 示例
    1. 常规计算(加减乘除)
    2. 四舍五入
      1. Round
        1. 句法
        2. 参数
        3. 返回值
        4. 例子
  4. 取整
  5. 安全值与最大数

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用)