| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402 |
- /**
- * @author zp
- */
- /**
- * 多平台一致精确计算库,比decimal更轻量更快
- * [Math.round、Math.min、Math.max,Math.floor、Math.ceil,这些系统方法一般情况下是可以放心使用的]
- */
- const ExactMath = Object.create(null);
- module.exports = ExactMath;
- // 计算精度
- // sin、cos、tan方法的误差小数点后16位
- const ACCURACY_SIN_ERROR = 1e-16;
- const ACCURACY_COS_ERROR = 1e-16;
- const ACCURACY_TAN_ERROR = 1e-16;
- // 角度弧度常量
- const DEG = 57.29577951308232;
- const RAD = 0.017453292519943295;
- // 系统常量
- ExactMath.PI = 3.141592653589793;
- ExactMath.E = 2.718281828459045;
- ExactMath.LN2 = 0.6931471805599453;
- ExactMath.LN10 = 2.302585092994046;
- ExactMath.LOG2E = 1.4426950408889634;
- ExactMath.LOG10E = 0.4342944819032518;
- ExactMath.SQRT1_2 = 0.7071067811865476;
- ExactMath.SQRT2 = 1.4142135623730951;
- /**
- * 链式调用
- * @example
- * const value = ExactMath.value(10).add(20.123).mul(2).sqrt().value;
- */
- let chain = null;
- ExactMath.value = function (value) {
- if (!chain) {
- chain = {
- value: 0,
- valueOf() { return this.value; },
- toString() { return String(this.value); }
- }
- for (const key in ExactMath) {
- if (key !== 'value' && typeof ExactMath[key] === 'function') {
- chain[key] = function (...args) {
- this.value = ExactMath[key].call(ExactMath, this.value, ...args);
- return this;
- }
- }
- }
- }
- chain.value = value;
- return chain;
- }
- /****************************************************基础****************************************************/
- /**
- * 获得小数位数
- * @param {Number} num 浮点数
- * @returns {Number}
- */
- ExactMath.getDecimalPlace = function (num) {
- if (num && num !== Math.floor(num)) {
- for (let n = 1, m = 10, temp = 0; n < 20; n += 1, m *= 10) {
- temp = num * m;
- if (temp == Math.floor(temp)) return n;
- }
- return 20;
- } else {
- return 0;
- }
- }
- /**
- * 保留n为小数,并四舍五入
- * @example
- * (2.335).toFixed(2)
- * ExactMath.toFixed(2.335, 2)
- * @param {Number} num 浮点数
- * @param {Number} n 整数
- * @returns {Number}
- */
- ExactMath.toFixed = function (num, n = 0) {
- if (n == 0) {
- return Math.round(num);
- } else {
- const m = Math.pow(10, n);
- return Math.round(num * (m * 10) / 10) / m;
- }
- }
- ExactMath.abs = function (x) {
- return Math.abs(x);
- }
- ExactMath.round = function (x) {
- return Math.round(x);
- }
- ExactMath.ceil = function (x) {
- return Math.ceil(x)
- }
- ExactMath.floor = function (x) {
- return Math.floor(x)
- }
- ExactMath.min = function (...args) {
- return Math.min(...args);
- }
- ExactMath.max = function (...args) {
- return Math.max(...args);
- }
- /**
- * 小数相加
- * @param {Number} num1 浮点数
- * @param {Number} num2 浮点数
- * @returns {Number}
- */
- ExactMath.add = function (...args) {
- if (args.length === 2) {
- const num1 = args[0];
- const num2 = args[1];
- const m = Math.pow(10, Math.max(this.getDecimalPlace(num1), this.getDecimalPlace(num2)));
- return (this.toFixed(num1 * m) + this.toFixed(num2 * m)) / m;
- } else {
- return args.reduce((a, b) => this.add(a, b))
- }
- };
- /**
- * 小数相减
- * @param {Number} num1 浮点数
- * @param {Number} num2 浮点数
- * @returns {Number}
- */
- ExactMath.sub = function (...args) {
- if (args.length === 2) {
- const num1 = args[0];
- const num2 = args[1];
- const m = Math.pow(10, Math.max(this.getDecimalPlace(num1), this.getDecimalPlace(num2)));
- return (this.toFixed(num1 * m) - this.toFixed(num2 * m)) / m;
- } else {
- return args.reduce((a, b) => this.sub(a, b))
- }
- };
- /**
- * 小数相乘
- * @param {Number} num1 浮点数
- * @param {Number} num2 浮点数
- * @returns {Number}
- */
- ExactMath.mul = function (...args) {
- if (args.length === 2) {
- let num1 = args[0];
- let num2 = args[1];
- // 方案1:
- // 直接相乘,但是相乘两数小数点过多会导致中间值[(n1 * m1) * (n2 * m2)]过大
- // const n1 = this.getDecimalPlace(num1);
- // const n2 = this.getDecimalPlace(num2);
- // const m1 = Math.pow(10, n1);
- // const m2 = Math.pow(10, n2);
- // return (n1 * m1) * (n2 * m2) / (m1 * m2);
- // 方案2:
- // 用除法实现乘法,不会存在过大中间值
- let n1 = this.getDecimalPlace(num1);
- let n2 = this.getDecimalPlace(num2);
- let m = Math.pow(10, n2);
- num2 = m / this.toFixed(num2 * m);
- m = Math.pow(10, Math.max(n1, this.getDecimalPlace(num2)));
- m = this.toFixed(num1 * m) / this.toFixed(num2 * m);
- let n = Math.min(this.getDecimalPlace(m), n1 + n2);
- return this.toFixed(m, n);
- } else {
- return args.reduce((a, b) => this.mul(a, b))
- }
- };
- /**
- * 小数相除法
- * @param {Number} num1 浮点数
- * @param {Number} num2 浮点数
- * @returns {Number}
- */
- ExactMath.div = function (...args) {
- if (args.length === 2) {
- const num1 = args[0];
- const num2 = args[1];
- const m = Math.pow(10, Math.max(this.getDecimalPlace(num1), this.getDecimalPlace(num2)));
- return this.toFixed(num1 * m) / this.toFixed(num2 * m);
- } else {
- return args.reduce((a, b) => this.div(a, b))
- }
- };
- /**
- * 取余
- * @param {Number} num1 浮点数
- * @param {Number} num2 浮点数
- * @returns {Number}
- */
- ExactMath.rem = function (...args) {
- if (args.length === 2) {
- const num1 = args[0];
- const num2 = args[1];
- const m = Math.pow(10, Math.max(this.getDecimalPlace(num1), this.getDecimalPlace(num2)));
- return this.toFixed(num1 * m) % this.toFixed(num2 * m) / m;
- } else {
- return args.reduce((a, b) => this.rem(a, b))
- }
- };
- /**
- * n次方,仅支持整数次方(正负都可以)
- * @param {Number} num 浮点数
- * @param {Number} n 整数
- */
- ExactMath.pow = function (num, n) {
- if (num == 0 && n == 0) {
- return 1;
- }
- if (num == 0 && n > 0) {
- return 0
- }
- if (num == 0 && n < 0) {
- return Infinity;
- }
- // num为负数,n为负小数,返回NaN
- if (num < 0 && n < 0 && Math.round(n) != n) {
- return NaN;
- }
- if (Math.round(n) != n) {
- throw new Error('n must be an integer');
- }
- let result = 1;
- if (n > 0) {
- for (let index = 0; index < n; index++) {
- result = this.mul(result, num);
- }
- } else if (n < 0) {
- for (let index = 0, len = Math.abs(n); index < len; index++) {
- result = this.div(result, num);
- }
- }
- return result;
- };
- /**
- * 开方运算【牛顿迭代法】
- *
- * @param {Number} n
- * @returns
- */
- ExactMath.sqrt = function (n) {
- if (n < 0) return NaN;
- if (n === 0) return 0;
- if (n === 1) return 1;
- let last = 0;
- let res = 1;
- let c = 50;
- while (res != last && --c >= 0) {
- last = res;
- res = this.div(this.add(res, this.div(n, res)), 2)
- }
- return res;
- // float InvSqrt(float x)
- // {
- // float xhalf = 0.5f * x;
- // int i = * (int *) & x; // get bits for floating VALUE
- // i = 0x5f375a86 - (i >> 1); // gives initial guess y0
- // x = * (float *) & i; // convert bits BACK to float
- // x = x * (1.5f - xhalf * x * x); // Newton step, repeating increases accuracy
- // x = x * (1.5f - xhalf * x * x); // Newton step, repeating increases accuracy
- // x = x * (1.5f - xhalf * x * x); // Newton step, repeating increases accuracy
- // return 1 / x;
- // }
- };
- /****************************************************随机****************************************************/
- function getSeed(seed) {
- if (isNaN(seed)) {
- seed = Math.floor(Math.random() * 233280);
- } else {
- seed = Math.floor(seed % 233280);
- }
- return seed;
- }
- let randomSeed = getSeed();
- /**
- * 设置随机种子
- */
- ExactMath.setSeed = function (seed) {
- randomSeed = getSeed(seed);
- };
- /**
- * 随机
- */
- ExactMath.random = function () {
- randomSeed = (randomSeed * 9301 + 49297) % 233280;
- return randomSeed / 233280.0;
- };
- /**
- * 根据随机种子随机
- * @param {number} seed
- */
- ExactMath.randomBySeed = function (seed) {
- seed = getSeed(seed);
- seed = (seed * 9301 + 49297) % 233280;
- return seed / 233280.0;
- };
- /****************************************************角度弧度转换****************************************************/
- /**
- * 弧度数转角度数
- * @param {Number} radians 浮点数
- * @returns {Numbe} 浮点数
- */
- ExactMath.radiansToDegrees = function (radians) {
- return this.div(radians, RAD);
- };
- /**
- * 角度数转弧度数
- * @param {Number} degrees 浮点数
- * @returns {Numbe} 浮点数
- */
- ExactMath.degreesToRadians = function (degrees) {
- return this.div(degrees, DEG);
- };
- /**
- * 将角度值转换到[0, 360)范围内
- * @param {Number} angle 浮点数
- * @returns {Number} 整数
- */
- ExactMath.get0To360Angle = function (angle) {
- if (angle === 0) {
- return 0;
- } else if (angle < 0) {
- return this.add(this.rem(angle, 360), 360);
- } else {
- return this.rem(angle, 360);
- }
- };
- /****************************************************三角函数****************************************************/
- /**
- * 查表
- */
- ExactMath._sin = {};
- ExactMath._cos = {};
- ExactMath._tan = {};
- /**
- * 3个三角函数,根据需求自行添加
- * 为了效率,应该尽量使用查表法
- * 表内查不到的,目前使用系统方法的结果并取前4位小数
- */
- ExactMath.sin = function (x) {
- if (this._sin.hasOwnProperty(x)) {
- return this._sin[x];
- }
- // if (x == 0) {
- // return 0;
- // } else if (x == 90) {
- // return 1;
- // }
- // let n = x, sum = 0, i = 1;
- // do {
- // i++;
- // sum = this.add(sum, n);
- // // n = -n * x * x / (2 * i - 1) / (2 * i - 2);
- // n = this.div(this.mul(-1, n, x, x), this.sub(this.mul(2, i), 1), this.sub(this.mul(2, i), 2));
- // } while (Math.abs(n) >= ACCURACY_SIN_ERROR);
- // return sum;
- return this.toFixed(Math.sin(x), 4);
- };
- ExactMath.cos = function (x) {
- if (this._cos.hasOwnProperty(x)) {
- return this._cos[x];
- }
- return this.toFixed(Math.cos(x), 4);
- };
- ExactMath.tan = function (x) {
- if (this._tan.hasOwnProperty(x)) {
- return this._tan[x];
- }
- return this.toFixed(Math.tan(x), 4);
- };
|