JS中的Number类型使用的是双精度浮点型,也就是其他语言中的double类型。在计算机内存中,单精度数是用32个 bit 来存储的浮点数,双精度数是使用64个 bit 来存储的浮点数。其中有1位符号位 (+/-),11位表示指数位(次方数),52位表示数值位(精确度) 内存结构如下:
在ES规范中规定e的范围在-1074 ~ 971,而m能表示的最大数是52个1,最小能表示的是1 。计算机中存储小数是先转换成二进制进行存储的,二进制的第一位有效数字必定是1,因此这个1不会被存储,可以节省一个存储位,因此尾数部分可以存储的范围是1 ~ 2^(52+1),也就是说Number能表示的最大数字绝对值范围是 2^-1074 ~ 2^(53+971) ->5e-324~1.7976931348623157e+308,可以通过Number.MAX_VALUE
和Number.MIN_VALUE
来得到证实。同样可以通过Number.MIN_SAFE_INTEGER
和Number.MAX_SAFE_INTEGER 证实
安全整数的范围。
1、在四则运算中存在精度丢失的问题,比如: 0.1 + 0.2 //0.30000000000000004
精度缺失不是小于或者大于某个值就会出现精度缺失,而是落在固定间隔长度的数值中就会精度缺失,精度缺失的原因是该数值在内存中不能完全记录下来。
前面提到,计算机中存储小数是先转换成二进制进行存储的,我们来看一下0.1和0.2转换成二进制的结果:
十进制(0.1) =>二进制 (00011001100110011001(1001)...)
十进制(0.2) => 二进制(00110011001100110011(0011)...)
可以发现,0.1和0.2转成二进制之后都是一个无限循环的数,前面提到尾数位只能存储最多53位有效数字,这时候就必须来进行四舍五入了,最终的这个二进制数转成十进制就是0.30000000000000004。
至此,这个精度丢失的问题已经解释清楚了,用一句话来概括就是,计算机中用二进制来存储小数,而大部分小数转成二进制之后都是无限循环的值,因此存在取舍问题,也就是精度丢失。
2、
超过最大安全整数的运算是不安全的,比如:9007199254740991 + 2 //9007199254740992
最大安全整数9007199254740991对应的二进制数如图:
53位有效数字都存储满了之后,想要表示更大的数字,就只能往指数数加一位,这时候尾数因为没有多余的存储空间,因此只能补0。 可以看出在指数位为53的情况下,最后一位尾数位为0的数字可以被精确表示,而最后一位尾数为为1的数字都不能被精确表示。也就是可以被精确表示和不能被精确表示的比例是1:1
。
小结:之所以会有最大安全整数这个概念,本质上还是因为数字类型在计算机中的存储结构。在尾数位不够补零之后,只要是多余的尾数为1所对应的整数都不能被精确表示。
可以发现,不管是浮点数计算的计算结果错误和大整数的计算结果错误,最终都可以归结到JS的精度只有53位(尾数只能存储53位的有效数字)。那么我们在日常工作中碰到这两个问题该如何解决呢?
3、JS的 toFixed() 方法错误问题
JS的 toFixed() 四舍五入规则与数学中的规则不同,而是采用银行家算法,俗称『四舍六入五成双』。对于位数很多的近似数,当有效位数确定后,其后面多余的数字应该舍去,只保留有效数字最末一位。
『四』:≤4 时舍去
『六』:≥6时进1
『五』:根据5后面的数字来定,当5后有数字时舍5进1;当5后无数字时分两种情况,①5前为奇数,舍5入1;②5前为偶数,舍5不进。
1、精度丢失在具体计算的体现:
// 加法 =====================
0.1 + 0.2 = 0.30000000000000004
0.7 + 0.1 = 0.7999999999999999
0.2 + 0.4 = 0.6000000000000001
// 减法 =====================
1.5 - 1.2 = 0.30000000000000004
0.3 - 0.2 = 0.09999999999999998
// 乘法 =====================
19.9 * 100 = 1989.9999999999998
0.8 * 3 = 2.4000000000000004
35.41 * 100 = 3540.9999999999995
// 除法 =====================
0.3 / 0.1 = 2.9999999999999996
0.69 / 10 = 0.06899999999999999
2、超过最大安全整数的运算或传参等场景
整数会出现误差,小数超出长度会被丢弃。(如绿谷后管入组号为number类型长度超出js安全数字,因不涉及到计算,所以改为string类型解决详情跳转异常)
var numLen16 = '999999999666666'
numLen16.length // 16 长度16位 正整数
+numLen16 // '999999999666666' 没有误差
var numLen17 = '9999999999555557'
numLen17.length // 17 长度17位 正整数
+numLen16 // '9999999999555556' 从第17位开始出现误差
//json 化时的问题
var json = JSON.stringify({a:999999999955555777,b:true})
json // "{"a":999999999955555800,"b":true}" 也会出现误差
//小数超过18位左右,小数部分超过位数会被丢掉
9555555555555.34243535 // 9555555555555.342
3、四舍五入算法问题
let num = 1.234
num.toFixed(2) //1.23 正确
let num = 1.235
num.toFixed(2) //1.24 正确
let num = 1.236
num.toFixed(2) //1.24 正确
let num = 0.234
num.toFixed(2) //0.23 正确
let num = 0.235
num.toFixed(2) //0.23 错误 X
let num = 0.236
num.toFixed(2) //0.24 正确
import { create, all } from 'mathjs'
// create a mathjs instance with configuration
const config = {
epsilon: 1e-12,
matrix: 'Matrix',
number: 'number',
precision: 64,
predictable: false,
randomSeed: null
}
const math = create(all, config)
// read the applied configuration
console.log(math.config())
// change the configuration
math.config({
number: 'BigNumber'
})
math.chain(value)
创建链。 math.chain(3)
.add(4)
.subtract(2)
.done() // 5
math.chain( [[1, 2], [3, 4]] )
.subset(math.index(0, 0), 8)
.multiply(3)
.done() // [[24, 6], [9, 12]]
import
函数在 mathjs 实例上可用,可以使用该create
函数创建。import
接受一个带有函数和变量的对象,或者一个带有工厂函数的数组。它具有以下语法: math.import(functions: Object [, options: Object])
> functions是包含要导入的函数和/或值的对象或数组。import支持常规值和函数、类型函数(参见部分类型函数)和工厂函数(参见部分工厂函数)。数组仅适用于包含工厂函数的情况。
> options是带有选项的可选第二个参数。以下选项可用:
· override 如果true,现有函数将被覆盖。默认值为false。
· silent 如果true,该函数不会在重复或无效类型上引发错误。默认值为false。
· wrap 如果true,则函数将被包装在一个包装函数中,该函数将 Matrix 等数据类型转换为 Array 等原始数据类型。使用不支持 math.js 数据类型的库扩展 math.js 时需要包装器。默认值为false。
// define new functions and variables
math.import({
myvalue: 42,
hello: function (name) {
return 'hello, ' + name + '!'
}
})
// defined functions can be used in both JavaScript as well as the parser
math.myvalue * 2 // 84
math.hello('user') // 'hello, user!'
const parser = math.parser()
parser.evaluate('myvalue + 10') // 52
parser.evaluate('hello("user")') // 'hello, user!'
扩展之导入外部库
可以按如下方式导入numbers.js和 numeric.js等外部库 。必须使用 npm 安装这些库:
$ npm install numbers
$ npm install numeric
import { create, all } from 'mathjs'
import * as numbers from 'numbers'
import * as numeric from 'numeric'
// create a mathjs instance and import the numbers.js and numeric.js libraries
const math = create(all)
math.import(numbers, {wrap: true, silent: true})
math.import(numeric, {wrap: true, silent: true})
// use functions from numbers.js
math.fibonacci(7) // 13
math.evaluate('fibonacci(7)') // 13
// use functions from numeric.js
math.evaluate('eig([1, 2; 4, 3])').lambda.x // [5, -1]
工厂函数:可以使用math.import
以下命令在 math.js 中导入常规 JavaScript 函数:
export function myFunction (a, b) {
// ...
}
import { myFunction } from './myFunction.js'
math.import({
myFunction
})
当myFunction
需要来自 math.js 的功能时会出现一个问题:当在单独的文件中时,它无法访问 math.js 的当前实例。可以使用工厂函数来解决这个问题。工厂函数允许在创建函数时将依赖项注入到函数中。
工厂函数的语法是:
factory(name: string, dependencies: string[], create: function, meta?: Object): function
> name 是创建的函数的名称。
> dependencies 是一个带有依赖函数名称的数组。
> create是创建函数的函数。具有依赖项的对象作为第一个参数传递。
> meta一个可选对象,可以包含您想要的任何元数据。这将作为meta已创建函数的属性附加。mathjs 实例使用的已知元数据属性是:
· isClass: boolean 如果为 true,则创建的函数应该是一个类,例如出于安全原因不会在表达式解析器中公开。
· lazy: boolean. 默认情况下,所有内容都由import. 只有在实际使用导入的函数或常量时,才会构造它。可以通过lazy: false在元数据中设置来强制立即创建函数。
· isTransformFunction: boolean. 如果为 true,则创建的函数将作为转换函数导入。它math本身不会被导入,只会在mathWithTransform表达式解析器使用的内部命名空间中导入。
· recreateOnConfigChange: boolean. 如果为 true,则在配置发生更改时将再次创建导入的工厂。例如,这用于诸如 之类的常量pi,这取决于number可以是数字或 BigNumber 的配置设置。
这里是一个工厂函数的例子,它依赖于multiply:
import { factory, create, all } from 'mathjs'
// create a factory function
const name = 'negativeSquare'
const dependencies = ['multiply', 'unaryMinus']
const createNegativeSquare = factory(name, dependencies, function ({ multiply, unaryMinus }) {
return function negativeSquare (x) {
return unaryMinus(multiply(x, x))
}
})
// create an instance of the function yourself:
const multiply = (a, b) => a * b
const unaryMinus = (a) => -a
const negativeSquare = createNegativeSquare({ multiply, unaryMinus })
console.log(negativeSquare(3)) // -9
// or import the factory in a mathjs instance and use it there
const math = create(all)
math.import(createNegativeSquare)
console.log(math.negativeSquare(4)) // -16
console.log(math.evaluate('negativeSquare(5)')) // -25
math.evaluate
评估表达式的函数。句法: math.evaluate(expr)
math.evaluate(expr, scope)
math.evaluate([expr1, expr2, expr3, ...])
math.evaluate([expr1, expr2, expr3, ...], scope)
函数evaluate
接受一个单一的表达式或一个带有表达式的数组作为第一个参数,并有一个可选的第二个参数,其中包含一个包含变量和函数的范围。scope可以是常规的 JavaScript 对象或 Map。scope将用于解析符号,并写入分配的变量或函数。
以下代码演示了如何计算表达式。
// evaluate expressions
math.evaluate('sqrt(3^2 + 4^2)') // 5
math.evaluate('sqrt(-4)') // 2i
math.evaluate('2 inch to cm') // 5.08 cm
math.evaluate('cos(45 deg)') // 0.7071067811865476
// provide a scope
let scope = {
a: 3,
b: 4
}
math.evaluate('a * b', scope) // 12
math.evaluate('c = 2.3 + 4.5', scope) // 6.8
scope.c // 6.8
表达式语法:math.js | an extensive math library for JavaScript and Node.js
简化、推导数学表达式:math.js | an extensive math library for JavaScript and Node.js
· 编译
Math.js 包含一个math.compile
将表达式编译为 JavaScript 代码的函数。这是首先解析然后编译表达式的快捷方式。语法是:
math.compile(expr)
math.compile([expr1, expr2, expr3, ...])
一个表达式只需要编译一次,之后就可以针对不同的作用域重复计算该表达式。可选作用域用于解析符号和编写分配的变量或函数。参数scope可以是一个普通的 Object 或 Map。
// 语法
const code = math.compile(expr) // compile an expression
const result = code.evaluate([scope]) // evaluate the code with an optional scope
// 用法示例
// parse an expression into a node, and evaluate the node
const code1 = math.compile('sqrt(3^2 + 4^2)')
code1.evaluate() // 5
· 解析
Math.js 包含一个math.parse
将表达式解析为 表达式树(https://mathjs.org/docs/expressions/expression_trees.html)的函数。语法是:
math.parse(expr)
math.parse([expr1, expr2, expr3, ...])
用法示例:
// parse an expression into a node, and evaluate the node
const node1 = math.parse('sqrt(3^2 + 4^2)')
const code1 = node1.compile()
code1.evaluate() // 5
// provide a scope
const node2 = math.parse('x^a')
const code2 = node2.compile()
let scope = {
x: 3,
a: 2
}
code2.evaluate(scope) // 9
// change a value in the scope and re-evaluate the node
scope.a = 3
code2.evaluate(scope) // 27
· 解析器
除了静态函数math.evaluate 之外 ,math.js 还包含一个带有函数evaluate的解析器parse,它会自动在内存中保留一个具有分配变量的作用域。解析器还包含一些方便的函数,用于从内存中获取、设置和删除变量。
解析器可以通过以下方式创建:
const parser = math.parser()
解析器包含以下功能:
> clear() 完全清除解析器的范围。
> evaluate(expr) 评估表达式。返回表达式的结果。
> get(name) 从解析器的作用域中检索变量或函数。
> getAll() 从解析器的作用域中检索包含所有已定义变量的映射。
> remove(name) 从解析器的范围中删除变量或函数。
> set(name, value) 在解析器的范围内设置变量或函数。
以下代码显示了如何创建和使用解析器:
// create a parser
const parser = math.parser()
// evaluate expressions
parser.evaluate('sqrt(3^2 + 4^2)') // 5
parser.evaluate('sqrt(-4)') // 2i
parser.evaluate('2 inch to cm') // 5.08 cm
parser.evaluate('cos(45 deg)') // 0.7071067811865476
// define variables and functions
parser.evaluate('x = 7 / 2') // 3.5
parser.evaluate('x + 3') // 6.5
parser.evaluate('f(x, y) = x^y') // f(x, y)
parser.evaluate('f(2, 3)') // 8
// get and set variables and functions
const x = parser.get('x') // x = 7
const f = parser.get('f') // function
const g = f(3, 3) // g = 27
parser.set('h', 500)
parser.evaluate('h / 2') // 250
parser.set('hello', function (name) {
return 'hello, ' + name + '!'
})
parser.evaluate('hello("user")') // "hello, user!"
// clear defined functions and variables
parser.clear()
数据类型
Math.js 支持三种类型的数字:
> 快速浮点运算的数字
> BigNumber 用于任意精度算术
> 分数,它根据分子和分母存储数字
· Number https://mathjs.org/docs/datatypes/numbers.html
Math.js 使用内置的 JavaScript Number 类型。Number 是一个浮点数,精度有限,为 64 位,大约 16 位。
舍入误差的解决方案:
>> 一种解决方案是将精度限制在显示输出中 16 位的实际精度以下:
// prevent round-off errors showing up in output
const ans = math.add(0.1, 0.2) // 0.30000000000000004
math.format(ans, {precision: 14}) // '0.3'
>> 替代方法是使用Fractions将数字存储为分子和分母,或使用BigNumbers存储具有更高精度的数字。0.1 + 0.2 == 0.3
在 JavaScript 中执行将返回 false,因为添加会0.1 + 0.2
引入舍入错误并且不会准确返回0.3
。为了解决这个问题,math.js 的关系函数会检查比较值之间的相对差异是否小于配置的 option epsilon
。 // compare values having a round-off error
console.log(0.1 + 0.2 === 0.3) // false
console.log(math.equal(0.1 + 0.2, 0.3)) // true
// small values (< 2.22e-16) cannot be compared
console.log(3e-20 === 3.1e-20) // false
console.log(math.equal(3e-20, 3.1e-20)) // true
· BigNumber math.js | an extensive math library for JavaScript and Node.js
对于任意精度的计算,math.js 支持BigNumber
数据类型。BigNumber 支持由decimal.js提供支持。
可以使用以下函数创建 BigNumber bignumber
:
math.bignumber('2.3e+500') // BigNumber, 2.3e+500
可以在实例化 math.js 时配置:
math.config({
number: 'BigNumber', // Default type of number:
// 'number' (default), 'BigNumber', or 'Fraction'
precision: 64 // Number of significant digits for BigNumbers
})
// use math
math.evaluate('0.1 + 0.2') // BigNumber, 0.3
math.js 中的大多数函数都支持 BigNumbers,但不是全部。例如,该函数random
不支持 BigNumbers。
BigNumber 的计算比 Number 的计算慢得多,但它们可以以任意精度执行。通过使用更高的精度,出现舍入误差的可能性更小:
// round-off errors with numbers
math.add(0.1, 0.2) // Number, 0.30000000000000004
math.divide(0.3, 0.2) // Number, 1.4999999999999998
// no round-off errors with BigNumbers :)
math.add(math.bignumber(0.1), math.bignumber(0.2)) // BigNumber, 0.3
math.divide(math.bignumber(0.3), math.bignumber(0.2)) // BigNumber, 1.5
BigNumber 并不能解决与精度和舍入误差相关的所有问题。具有无限位数的数字不能用常规数字或 BigNumber 表示。虽然 BigNumber 可以存储更多的数字,但如果只是为了保持计算速度足够快以保持实用性,数字的数量仍然有限。
BigNumber 转换为数字时,BigNumber 的高精度将丢失。当 BigNumber 太大而无法表示为 Number 时,它将被初始化为Infinity
。
const one = math.bignumber(1)
const three = math.bignumber(3)
const third = math.divide(one, three)
console.log(third.toString())
// outputs 0.3333333333333333333333333333333333333333333333333333333333333333
const ans = math.multiply(third, three)
console.log(ans.toString())
// outputs 0.9999999999999999999999999999999999999999999999999999999999999999
// this should be 1 again, but `third` is rounded to a limited number of digits 3
· Fraction
math.js 支持一种Fraction
数据类型。分数支持由fraction.js提供支持。与numbers和BigNumbers不同,分数可以存储具有无限重复小数的数字,例如1/3 = 0.3333333...
,可以表示为0.(3)
,或者2/7
可以表示为0.(285714)
。
// 可以使用以下函数创建分数fraction
math.fraction('1/3') // Fraction, 1/3
math.fraction(2, 3) // Fraction, 2/3
math.fraction('0.(3)') // Fraction, 1/3
// 也可以在函数中使用fraction
math.add(math.fraction('1/3'), math.fraction('1/6')) // Fraction, 1/2
math.multiply(math.fraction('1/4'), math.fraction('1/2')) // Fraction, 1/8
// 可以在实例化 math.js 时配置
// Configure the default type of number: 'number' (default), 'BigNumber', or 'Fraction'
math.config({
number: 'Fraction'
})
// use the expression parser
math.evaluate('0.32 + 0.08') // Fraction, 2/5
· Matrices math.js | an extensive math library for JavaScript and Node.js
Math.js 支持多维矩阵和数组。矩阵可以被创建、操作和用于计算。常规 JavaScript 数组以及由 math.js 实现的矩阵类型都可以在所有相关的 math.js 函数中互换使用。math.js 支持密集和稀疏矩阵。
Math.js 支持两种类型的矩阵:
> Array,一个常规的 JavaScript 数组。可以通过嵌套数组来创建多维数组。
> Matrix,由 math.js 实现的矩阵。阿Matrix是缠绕在常规的JavaScript对象Array,提供效用函数,便于矩阵运算例如subset,size,resize,clone,等等。
// create an array and a matrix
const array = [[2, 0], [-1, 3]] // Array
const matrix = math.matrix([[7, 1], [-2, 3]]) // Matrix
// perform a calculation on an array and matrix
math.square(array) // Array, [[4, 0], [1, 9]]
math.square(matrix) // Matrix, [[49, 1], [4, 9]]
// perform calculations with mixed array and matrix input
math.add(array, matrix) // Matrix, [[9, 1], [-3, 6]]
math.multiply(array, matrix) // Matrix, [[14, 2], [-13, 8]]
// create a matrix. Type of output of function ones is determined by the
// configuration option `matrix`
math.ones(2, 3) // Matrix, [[1, 1, 1], [1, 1, 1]]
有许多函数可以创建具有特定大小和内容的矩阵:ones
、zeros
、identity
。
// zeros creates a matrix filled with zeros
math.zeros(3) // Matrix, size [3], [0, 0, 0]
math.zeros(3, 2) // Matrix, size [3, 2], [[0, 0], [0, 0], [0, 0]]
math.zeros(2, 2, 2) // Matrix, size [2, 2, 2],
// [[[0, 0], [0, 0]], [[0, 0], [0, 0]]]
// ones creates a matrix filled with ones
math.ones(3) // Matrix, size [3], [1, 1, 1]
math.multiply(math.ones(2, 2), 5) // Matrix, size [2, 2], [[5, 5], [5, 5]]
// identity creates an identity matrix
math.identity(3) // Matrix, size [3, 3], [[1, 0, 0], [0, 1, 0], [0, 0, 1]]
math.identity(2, 3) // Matrix, size [2, 3], [[1, 0, 0], [0, 1, 0]]
· Units math.js | an extensive math library for JavaScript and Node.js
Math.js 支持单位。单位可用于计算和转换。
const a = math.unit(45, 'cm') // Unit 450 mm
const b = math.unit('0.1 kilogram') // Unit 100 gram
const c = math.unit('2 inch') // Unit 2 inch
const d = math.unit('90 km/h') // Unit 90 km/h
const e = math.unit('101325 kg/(m s^2)') // Unit 101325 kg / (m s^2)
const d = c.to('cm') // Unit 5.08 cm
b.toNumber('gram') // Number 100
math.number(b, 'gram') // Number 100
c.equals(a) // false
c.equals(d) // true
c.equalBase(a) // true
c.equalBase(b) // false
d.toString()
可以使用该math.createUnit
函数将自己的单位添加到 Math.js。以下示例定义了一个新单位furlong
,然后在计算中使用用户定义的单位:
math.createUnit('furlong', '220 yards')
math.evaluate('1 mile to furlong') // 8 furlong
math.format(value)
math.format(value, options)
math.format(value, precision)
math.format(value, callback)
> value: * 要格式化的值
> options: Object 具有格式选项的对象。可用选项:
· notation: string 数字符号。从中选择:
· 'fixed' 始终使用常规数字表示法。例如“123.40”和“14000000”
· 'exponential' 总是使用指数表示法。例如“1.234e+2”和“1.4e+7”
· 'engineering' 始终使用工程表示法:始终使用指数表示法,并选择指数为 3 的倍数。例如 '123.4e+0' 和 '14.0e+6'
· 'auto'(默认值)用于在lower和之间具有绝对值的数字的常规数字符号 upper,并在其他地方使用指数符号。包括下限,排除上限。例如“123.4”和“1.4e7”。
· 'bin'、'oct' 或 'hex' 使用二进制、八进制或十六进制表示法格式化数字。例如“0b1101”和“0x10fe”。
· wordSize: number 用于以二进制、八进制或十六进制表示法进行格式化的字长(以位为单位)。仅与 'notation' 选项的 'bin'、'oct' 或 'hex' 值一起使用。定义此选项后,该值将格式化为给定字大小的有符号二进制补码整数,并将大小后缀附加到输出。例如 format(-1, {notation: 'hex', wordSize: 8}) === '0xffi8'。默认值未定义。
· precision: number 一个介于 0 和 16 之间的数字,用于舍入数字的数字。在符号“指数”、“工程”和“自动”的情况下,precision 定义返回的有效数字的总数。在符号“固定”的情况下,precision定义小数点后的有效位数。 precision默认情况下未定义。
· lowerExp: number 指数确定在 时用指数格式化值的下限notation='auto。默认值为-3。
· upperExp: number 确定在 时用指数格式化值的上限的指数notation='auto。默认值为5。
· fraction: string. 可用值:“比率”(默认)或“十进制”。例如format(fraction(1, 3))配置'ratio'时输出'1/3',配置0.(3)'decimal'时输出。
> callback: function 自定义格式化函数,为 中的所有数字元素调用value,例如矩阵的所有元素,或复数的实部和虚部。此回调可用于覆盖具有任何类型格式的内置数字符号。函数callback 使用value作为参数调用并且必须返回一个字符串。
import { create, all } from 'mathjs';
const config = {
epsilon: 1e-12,
matrix: 'Matrix', // 函数的默认矩阵输出类型。
number: 'BigNumber', // BigNumbers比 JavaScript 的默认数字具有更高的精度
precision: 64, // BigNumbers 的最大有效数字位数。此设置仅适用于 BigNumber,不适用于数字。
predictable: false, // 可预测的输出类型函数。当为真时,输出类型仅取决于输入类型。当为 false(默认)时,输出类型可能因输入值而异。
randomSeed: null // 设置为null使用随机种子为伪随机数生成器提供种子。
};
const math = create(all, config);
math.import({
forEg: function (number, n) {}
});
// tofixed
export function forEg (number, digits) {
return math.forEg(number, digits);
}
// +
export function add (a, b, digits) {
let num1 = a ? math.bignumber(a) : 0;
let num2 = b ? math.bignumber(b) : 0; // bignumber 保证计算精度 format 返回计算的数字表示法
let digit = digits || 64;
return math
.format(math
.chain(math
.add(num1, num2)
)
.done()
, {notation: 'fixed', precision: digit});
}
// -
export function subtract (a, b, digits) {
let num1 = a ? math.bignumber(a) : 0;
let num2 = b ? math.bignumber(b) : 0; // bignumber 保证计算精度 format 返回计算的数字表示法
let digit = digits || 64;
return math
.format(math
.chain(math
.subtract(num1, num2)
)
.done()
, {notation: 'fixed', precision: digit});
}
// x
export function multiply (a, b, digits) {
let num1 = a ? math.bignumber(a) : 0;
let num2 = b ? math.bignumber(b) : 0; // bignumber 保证计算精度 format 返回计算的数字表示法
let digit = digits || 64;
return math
.format(math
.chain(math
.multiply(num1, num2)
)
.done()
, {notation: 'fixed', precision: digit});
}
// ÷
export function divide (a, b, digits) {
let num1 = a ? math.bignumber(a) : 0;
let num2 = b ? math.bignumber(b) : 0; // bignumber 保证计算精度 format 返回计算的数字表示法
let digit = digits || 64;
return math
.format(math
.chain(math
.divide(num1, num2)
)
.done()
, {notation: 'fixed', precision: digit});
}
// 总价=数量*进价
export function TotalPrice (num, price, digits) {
let digit = digits || 64;
return math
.format(math.chain(num)
.multiply(price)
.done()
, {notation: 'fixed', precision: digit});
}
// 无税金额=数量*进价/(1+进项税率)
export function NoTaxTotalPrice (num, price, tax, digits) {
let digit = digits || 64;
return math
.format(math.chain(
multiply(num, price))
.divide(add(1, tax))
.done(), {notation: 'fixed', precision: digit});
}
// 税额=金额-无税金额
export function TaxTotalPrice (num, price, tax, digits) {
let digit = digits || 64;
let TotalPrice = multiply(num, price); // 金额
let Tax = add(1, tax); // 税率
let NoTaxTotalPrice = divide(TotalPrice, Tax); // 无税金额
return math
.format(math.chain(TotalPrice)
.subtract(NoTaxTotalPrice)
.done(), {notation: 'fixed', precision: digit});
}
// 总价=数量*进价 计算结果 1851.8518368->1851.85
console.log('总价1851.85', mathSelf.TotalPrice(15, 123.45678912, 2));
// 无税金额=数量*进价/(1+进项税率)计算结果:1833.51667009901->1833.52
console.log('无税金额1833.52', mathSelf.NoTaxTotalPrice(15, 123.45678912, 0.01, 2));
// 税额=金额-无税金额 计算结果 18.33516670099->18.34
console.log('税额18.34', mathSelf.TaxTotalPrice(15, 123.45678912, 0.01, 2));
// 计算结果 0.30
console.log('精度验证0.1+0.2', mathSelf.add(0.1, 0.2, 2));
// 计算结果 1990.00
console.log('精度验证19.9*100', mathSelf.multiply(19.9, 100, 2));