|
@@ -1,5 +1,7 @@
|
|
|
/**
|
|
|
* Created by jimiz on 2017/3/28.
|
|
|
+ * 经验证:10000次四舍五入,用num.toFixed为15毫秒,用roundTo为47毫秒,速度低一些,但可以接受
|
|
|
+ * 另:经手工验证,用num.toString(2)将十进制浮点数转换为二进制浮点数时,最后一位有错误的情况出现,例子(10.0311)
|
|
|
*/
|
|
|
|
|
|
let scMathUtil = {
|
|
@@ -7,18 +9,93 @@ let scMathUtil = {
|
|
|
let lFactor = Math.pow(10, digit);
|
|
|
let fOffSet = 0.5;
|
|
|
let sign = '';
|
|
|
+ // 处理符号
|
|
|
if (num < 0){
|
|
|
sign = '-';
|
|
|
num = Math.abs(num);
|
|
|
}
|
|
|
+ // 计算
|
|
|
let result = Math.floor((num / lFactor) + fOffSet).toString();
|
|
|
let iLength = result.length;
|
|
|
+ // 因为数值被转为整数计算,当目标位数在小数点后,且数值小于0,需要补齐前面的位数
|
|
|
+ if (iLength < -digit){
|
|
|
+ result = this.zeroString(-digit) + result;
|
|
|
+ }
|
|
|
+ // 当目标位数在小数点前,需要补齐后面的位数
|
|
|
+ else if ((digit > 0) && (iLength < digit)){
|
|
|
+ result = result + this.zeroString(digit);
|
|
|
+ }
|
|
|
+ iLength = result.length;
|
|
|
+ // 获得小数点前的数字
|
|
|
let r1 = result.substring(0, iLength + digit);
|
|
|
+ // 获得小数点后的数字
|
|
|
let r2 = result.substring(iLength + digit, iLength);
|
|
|
+ // 拼出完整结果
|
|
|
return Number(sign + r1 + '.' + r2);
|
|
|
},
|
|
|
+ // 原来直接用num.toString(2),如果小数段最后位数是0,会被舍掉,导致进位计算bug
|
|
|
+ // 改为自己计算二进制,固定为53位。
|
|
|
+ // 经验证速度没有差别
|
|
|
+ // 另:经手工验证,用num.toString(2)将十进制浮点数转换为二进制浮点数时,最后一位有错误的情况出现,例子(10.0311)
|
|
|
floatToBin: function(num) {
|
|
|
- return num.toString(2);
|
|
|
+ let sign = '';
|
|
|
+ let dNum = num;
|
|
|
+ // 符号位
|
|
|
+ if (num < 0) {
|
|
|
+ sign = '-';
|
|
|
+ dNum = -num;
|
|
|
+ };
|
|
|
+ // 解析整数段
|
|
|
+ let iNum = Math.floor(dNum);
|
|
|
+ let iFactor;
|
|
|
+ let sResult1 = '';
|
|
|
+ // 计算二进制整数段
|
|
|
+ while (iNum > 0){
|
|
|
+ iFactor = iNum % 2;
|
|
|
+ iNum = Math.floor(iNum / 2);
|
|
|
+ sResult1 = iFactor + sResult1;
|
|
|
+ }
|
|
|
+ // 判断是否有整数段
|
|
|
+ let bIntZero = sResult1 === '';
|
|
|
+ if (bIntZero){
|
|
|
+ sResult1 = '0';
|
|
|
+ }
|
|
|
+ // 解析小数段
|
|
|
+ let fNum = dNum - Math.floor(dNum);
|
|
|
+ let sResult2 = '';
|
|
|
+ if (fNum > 0){
|
|
|
+ // 双精度浮点数,尾数总长52位,因为第一位总是1,存储时已经被隐藏,所以有效位数为53位
|
|
|
+ const floatLength = 53;
|
|
|
+
|
|
|
+ let iLength;
|
|
|
+ // js的bug,浮点数直接取小数可能不能获得精确值,只有转成字符串,截取字符串中的小数段
|
|
|
+ let sNum = dNum.toString(10);
|
|
|
+ let iDot = sNum.indexOf('.');
|
|
|
+ sNum = '0' + sNum.substring(iDot, sNum.length);
|
|
|
+ fNum = Number(sNum);
|
|
|
+ // 有整数段,则小数位数为全部位数-整数位数
|
|
|
+ if (!bIntZero){
|
|
|
+ iLength = floatLength - sResult1.length;
|
|
|
+ }
|
|
|
+ else{
|
|
|
+ iLength = floatLength;
|
|
|
+ }
|
|
|
+ // 计算二进制小数段
|
|
|
+ while (iLength > 0){
|
|
|
+ fNum = fNum * 2;
|
|
|
+ iFactor = Math.floor(fNum);
|
|
|
+ fNum = fNum % 1;
|
|
|
+ sResult2 = sResult2 + iFactor;
|
|
|
+ if (iFactor > 0){
|
|
|
+ bIntZero = false;
|
|
|
+ }
|
|
|
+ if (bIntZero && (iFactor === 0)){
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ iLength--;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return sign + sResult1 + '.' + sResult2;
|
|
|
},
|
|
|
binToFloat: function(bin) {
|
|
|
let result = 0;
|
|
@@ -60,13 +137,7 @@ let scMathUtil = {
|
|
|
let result = bin;
|
|
|
let iDot = bin.indexOf('.');
|
|
|
if (iDot < 0){return result};
|
|
|
- // 二进制浮点数带小数位数最大长度
|
|
|
- let floatLength = 53;
|
|
|
let iLength = bin.length;
|
|
|
- // 长度小于53说明该二进制数尾数长度小于Double类型限制,未被截断,无需进位
|
|
|
- if (iLength < floatLength) {
|
|
|
- return result;
|
|
|
- }
|
|
|
iLength = bin.length;
|
|
|
for (let i = iLength - 1; i > iDot; i--){
|
|
|
let num = Number(bin[i]);
|
|
@@ -84,5 +155,27 @@ let scMathUtil = {
|
|
|
roundTo: function(num, digit){
|
|
|
let me = this;
|
|
|
return me.innerRoundTo(me.binToFloat(me.incMantissa(me.floatToBin(num))), digit);
|
|
|
+ },
|
|
|
+ isNumber : function (obj) {
|
|
|
+ return obj === +obj;
|
|
|
+ },
|
|
|
+ roundToString:function(obj,decimal){
|
|
|
+ let me = this;
|
|
|
+ let value;
|
|
|
+ if(me.isNumber(obj)){
|
|
|
+ value = me.roundTo(obj,-decimal)
|
|
|
+ }else {
|
|
|
+ value = me.roundTo(Number(obj),-decimal);
|
|
|
+ }
|
|
|
+ return value.toFixed(decimal);
|
|
|
}
|
|
|
+};
|
|
|
+
|
|
|
+Number.prototype.toDecimal = function (ADigit) {
|
|
|
+ //return parseFloat(this.toFixed(ADigit));
|
|
|
+ digit = (ADigit && typeof(ADigit) === 'number' && Number.isInteger(ADigit) && ADigit >= 0) ? -ADigit : -2;
|
|
|
+ // var s = scMathUtil.roundTo(this, digit);
|
|
|
+ // console.log('Number: ' + this + ' Digit: ' + digit + ' Result: ' + s);
|
|
|
+ // return parseFloat(s);
|
|
|
+ return scMathUtil.roundTo(this, digit);
|
|
|
};
|