在制作帧同步游戏时,使用浮点数进行同步时是一种运气同步,因为误差积累和战斗时长会导致不同步,所以要使用定点数.JS上最受欢迎功能最全的定点数库是decimal.js.但是decimal虽然功能强大,计算精度高,但是性能低下.所以我使用了计算精度合适的乘除法方案.
源码
/**
* 定点数,比decimal更轻量更快
*/
export default class Fixed {
// 角度弧度常量
public static DEG = 57.29577951308232;
public static RAD = 0.017453292519943295;
// 系统常量
public static PI = 3.141592653589793;
public static E = 2.718281828459045;
public static LN2 = 0.6931471805599453;
public static LN10 = 2.302585092994046;
public static LOG2E = 1.4426950408889634;
public static LOG10E = 0.4342944819032518;
public static SQRT1_2 = 0.7071067811865476;
public static SQRT2 = 1.4142135623730951;
public static Chain: Fixed = null;
// 缩放的比例
public static Ratio = 1000;
// 保留小数的位数
public static Decimals = 3;
public value: number;
public valueOf() {
return this.value;
}
public toString() {
return String(this.value);
}
/**
* 链式调用
* @example
* const value = exactMath.value(10).add(20.123).mul(2).sqrt().value;
*/
public static value(value): Fixed{
if (!Fixed.Chain) {
Fixed.Chain = new Fixed();
}
Fixed.Chain.value = value;
return Fixed.Chain;
}
/**
* 保留n为小数,并四舍五入
* @example
* (2.335).toFixed(2)
* exactMath.toFixed(2.335, 2)
* @param {Number} num 浮点数
* @param {Number} n 整数
* @returns {Number}
*/
public static toFixed(num: number, n = 0) {
if (n == 0) {
return Math.round(num);
} else {
const m = Math.pow(10, n);
return Math.round(num * (m * 10) / 10) / m;
}
}
/**
* 获得小数位数
* @param {Number} num 浮点数
* @returns {Number}
*/
public static 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;
}
}
/**
* 小数相加
*/
public add (num: number): Fixed {
this.value = (Math.floor(this.value * Fixed.Ratio) + Math.floor(num * Fixed.Ratio)) / Fixed.Ratio
return this;
};
/**
* 小数相减
*/
public sub (num: number): Fixed {
this.value = (Math.floor(this.value * Fixed.Ratio) - Math.floor(num * Fixed.Ratio)) / Fixed.Ratio
return this;
};
/**
* 小数相乘
*/
public mul (num: number): Fixed {
this.value = (Math.floor(this.value * Fixed.Ratio) * Math.floor(num * Fixed.Ratio)) / Fixed.Ratio / Fixed.Ratio;
return this;
};
/**
* 小数相除
*/
public div (num: number): Fixed {
this.value = (Math.floor(this.value * Fixed.Ratio) / Math.floor(num * Fixed.Ratio));
return this;
};
/**
* 取余
*/
public rem (num: number): number {
const m = Math.pow(10, Math.max(Fixed.getDecimalPlace(this.value), Fixed.getDecimalPlace(num)));
return Fixed.toFixed(this.value * m) % Fixed.toFixed(num * m) / m;
};
/**
* 幂
*/
public pow (num: number): Fixed {
this.value = Math.pow(Fixed.toFixed(this.value, Fixed.Decimals), Fixed.toFixed(this.value, num));
return this;
};
/**
* 开方
*/
public sqrt (): Fixed {
this.value = Math.sqrt(Fixed.toFixed(this.value, Fixed.Decimals));
return this;
};
/**
* 三角函数
*/
public sin (x: number): Fixed {
this.value = Fixed.toFixed(Math.sin(x), Fixed.Decimals);
return this;
};
public cos (x: number): Fixed {
this.value = Fixed.toFixed(Math.cos(x), Fixed.Decimals);
return this;
};
public tan (x: number): Fixed {
this.value = Fixed.toFixed(Math.tan(x), Fixed.Decimals);
return this;
};
public asin (x: number): Fixed {
this.value = Fixed.toFixed(Math.asin(x), Fixed.Decimals);
return this;
};
public acos (x: number): Fixed {
this.value = Fixed.toFixed(Math.acos(x), Fixed.Decimals);
return this;
};
public atan (x: number): Fixed {
this.value = Fixed.toFixed(Math.atan(x), Fixed.Decimals);
return this;
};
}
使用
使用链式进行计算
Fixed.value(1000.456789).add(200.123456).mul(100).div(2).add(100).sub(2).div(2).value;
性能测试
log("乘除法定点数库");
var time = Date.now();
for (var i = 0; i < 1000000; i++) {
Fixed.value(1000.456789).add(200.123456).mul(100).div(2).add(100).sub(2).div(2).value;
}
var dTime = Date.now() - time;
log("消耗时间:" + dTime);
log("计算结果:" + Fixed.value(1000.456789).add(200.123456).mul(100).div(2).add(100).sub(2).div(2).value)
log("原生浮点数运算");
var time1 = Date.now();
for (var i = 0; i < 1000000; i++) {
let a = ((1000.456789+ 200.123456) * 100 / 2 + 100 - 2) / 2;
}
var dTime1 = Date.now() - time1;
log("消耗时间:" + dTime1);
log("计算结果:" + ((1000.456789+ 200.123456) * 100 / 2 + 100 - 2) / 2)
log("Decimal定点数库");
var time2 = Date.now();
for (var i = 0; i < 1000000; i++) {
new Decimal(1000.456789).add(200.123456).mul(100).div(2).add(100).sub(2).div(2).toNumber();
}
var dTime2 = Date.now() - time2;
log("消耗时间:" + dTime2);
log("计算结果:" + new Decimal(1000.456789).add(200.123456).mul(100).div(2).add(100).sub(2).div(2).toNumber().valueOf());
乘除法定点数库
消耗时间:63
计算结果:30063.475
原生浮点数运算
消耗时间:5
计算结果:30063.506124999996
Decimal定点数库
消耗时间:3245
计算结果:30063.506125