Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JS基础测试33 #29

Open
zhangxinxu opened this issue Jun 12, 2019 · 21 comments
Open

JS基础测试33 #29

zhangxinxu opened this issue Jun 12, 2019 · 21 comments

Comments

@zhangxinxu
Copy link
Owner

一转眼又到周三了,本期小测题目如下,围绕toFixed()方法展开:

大家提交回答的时候,注意缩进距离,起始位置从左边缘开始;另外,github自带代码高亮,所以请使用下面示意的格式。

```js
// 你的JS代码写在这里
 ```

其它:

  1. 首位答题者可获得额外2积分和直播翻牌机会;
  2. 本次答疑直播为6月15日上午10:00,会和上期CSS小测一起答疑,大约40多分钟;
@Seasonley
Copy link

Seasonley commented Jun 12, 2019

  1. "1"
  2. "2"
  3. "0.04"
  4. "0.04"
  5. Number.prototype.toFixed=function(v){return (this>0?1:-1)*parseInt(Math.round(Math.abs(this) * Math.pow(10,v))) / Math.pow(10,v)}
  6. String.prototype.toFixed=function(v){return (this>0?1:-1)*parseInt(Math.round(Math.abs(this) * Math.pow(10,v))) / Math.pow(10,v)}
//zxx: 1. 应该返回字符串;2. 应该补全末尾的0

@wingmeng
Copy link

wingmeng commented Jun 12, 2019

Chrome Firefox IE11
0.6.toFixed(0) 1 1 1
1.6.toFixed(0) 2 2 2
0.035.toFixed(2) 0.04 0.04 0.04
0.045.toFixed(2) 0.04 0.04 0.05
Number.prototype.toFixed = myToFixed;
String.prototype.toFixed = myToFixed;

function myToFixed(digits = 0) {
  let num = String(this);

  // 小数位不足时后置补零
  const postfixZero = s => {
    let len = digits;

    if (s.indexOf('.') > 0) {
      len = digits - s.split('.')[1].length;
    } else if (len > 0) {
      s += '.';  // 整数位后补小数点
    }

    return s + '0'.repeat(len);
  };

  digits = parseInt(digits, 10);

  if (isNaN(digits)) {  // 非数值
    throw new Error('digits argument must be a number(or string number)');
  }

  if (digits < 0) {  // 负数
    throw new Error('digits argument must be greater than or equal to 0');
  }

  if (!isFinite(num)) {  // +-Infinity
    return num;
  }

  if (num.indexOf('.') > 0) {
    let times = Math.pow(10, digits);  // 根据小数位长度获取升幂倍数
    num = Math.round(num * times) / times + '';  // 四舍五入,降幂
  }

  return postfixZero(num);
}

测试用例:

console.group('普通场景测试');
  console.log(0.6.toFixed(0));  // "1"
  console.log(1.6.toFixed(0));  // "2"
  console.log(0.035.toFixed(2));  // "0.04"
  console.log(0.045.toFixed(2));  // "0.05"
console.groupEnd();    
  
console.group('进阶场景测试');
  console.log(Math.PI.toFixed(10));  // "3.1415926536"
  console.log(0.9.toFixed());  // "1"
  console.log(Number(5).toFixed(2));  // "5.00"
  console.log(3..toFixed(3));  // "3.000"
  console.log(.5.toFixed('3'));  // "0.500"
  console.log(1.2345.toFixed(2.6));  // "1.23"
console.groupEnd();
  
console.group('参数异常测试');
  console.log(Infinity.toFixed(5));  // "Infinity"
  console.log(0.5.toFixed(-2));  // Error: digits argument must be greater than or equal to 0
  console.log(0.5.toFixed(null));  // Error: digits argument must be a number(or string number)
console.groupEnd();
//zxz:  滴~满分~

2019-07-21:无法通过 1.005.toFixed(2) 的测试用例,期望返回 1.01 实际返回 1.00

@silverWolf818
Copy link

silverWolf818 commented Jun 12, 2019

  1. 1
  2. 2
  3. 0.04
  4. 0.05
Number.prototype.toFixed = String.prototype.toFixed = function (number) {
    if(isNaN(number)) return;
    return parseInt(this * Math.pow(10, number) + 0.5) / Math.pow(10, number);
}
//zxx: 1. 应该返回字符串;2. 应该补全末尾的0

@guqianfeng
Copy link

参考了这个被科普了ie和谷歌的区别

以谷歌浏览器作答

  1. 1
  2. 2
  3. 0.04
  4. 0.04
    String.prototype.toFixed = Number.prototype.toFixed = function (fractionDigits) {
        var num = this;
        if(isNaN(num)){
            throw Error(`${num}不是一个数字`);
        }
        return Math.round(num * Math.pow(10, fractionDigits)) / Math.pow(10, fractionDigits);
    };

@NeilChen4698
Copy link

NeilChen4698 commented Jun 12, 2019

  1. '1'
  2. '2'
  3. '0.04'
  4. '0.04' (Chrome); '0.05' (IE)
Number.prototype.toFixed = function(d) {
	d = isNaN(d) ? 0 : parseInt(d);
	if (d < 0 || d > 20) {
        throw new RangeError('toFixed() digits argument must be between 0 and 20');
    }
    var val = this.toString();
    if (val.indexOf('.') < 0) {
    	return val;
    }
    if (d === 0) {
    	return val.replace(RegExp('^([0-9]+)(?:\\.)([0-9])[0-9]*$'), function(m,intPart, judgePart) {
    		return (+judgePart >= 5 ? ((+intPart + 1) + '') : intPart);
    	});
    } else {
	    return val.replace(RegExp('^([0-9]+\\.)([0-9]*)$'), function(m, intPart, fractionalPart) {
	    	return intPart + (fractionalPart.length < d ? (fractionalPart + Array(d - fractionalPart.length + 1).join('0')) : fractionalPart);
	    }).replace(RegExp('^([0-9]+)(\\.)([0-9]{' + d + '})([0-9])[0-9]*$'), function(m, intPart, dotPart, keepPart, judgePart) {
	    	var orgLen = keepPart.length;
	    	var newVal = (+judgePart >= 5 ? (+keepPart + 1) : +keepPart).toString();
	    	if (newVal.length > orgLen) {
	    		return (+intPart + (+newVal.slice(0,1))) + dotPart + newVal.slice(1);
	    	} else if (newVal.length < orgLen) {
	    		return intPart + dotPart + Array(orgLen - newVal.length + 1).join('0') + newVal;
	    	} else {
	    		return intPart + dotPart + newVal;
	    	}
	    });
    }
};
String.prototype.toFixed = function(d) {
	if (isNaN(this)) {
		return NaN;
	}
	d = isNaN(d) ? 0 : parseInt(d);
	if (d < 0 || d > 20) {
        throw new RangeError('toFixed() digits argument must be between 0 and 20');
    }
    var val = (+this).toString();
    if (val.indexOf('.') < 0) {
    	return val;
    }
    if (d === 0) {
    	return val.replace(RegExp('^([0-9]+)(?:\\.)([0-9])[0-9]*$'), function(m,intPart, judgePart) {
    		return (+judgePart >= 5 ? ((+intPart + 1) + '') : intPart);
    	});
    } else {
	    return val.replace(RegExp('^([0-9]+\\.)([0-9]*)$'), function(m, intPart, fractionalPart) {
	    	return intPart + (fractionalPart.length < d ? (fractionalPart + Array(d - fractionalPart.length + 1).join('0')) : fractionalPart);
	    }).replace(RegExp('^([0-9]+)(\\.)([0-9]{' + d + '})([0-9])[0-9]*$'), function(m, intPart, dotPart, keepPart, judgePart) {
	    	var orgLen = keepPart.length;
	    	var newVal = (+judgePart >= 5 ? (+keepPart + 1) : +keepPart).toString();
	    	if (newVal.length > orgLen) {
	    		return (+intPart + (+newVal.slice(0,1))) + dotPart + newVal.slice(1);
	    	} else if (newVal.length < orgLen) {
	    		return intPart + dotPart + Array(orgLen - newVal.length + 1).join('0') + newVal;
	    	} else {
	    		return intPart + dotPart + newVal;
	    	}
	    });
    }
};

@liyongleihf2006
Copy link
Contributor

liyongleihf2006 commented Jun 12, 2019

—— chrome firefox ie
0.6 1 1 1
1.6 2 2 2
0.035 0.04 0.04 0.04
0.045 0.04 0.04 0.05

jsbin

Number.prototype.toFixed = __toFixed;
String.prototype.toFixed = __toFixed;
console.log(0.6.toFixed(0));
console.log(1.6.toFixed(0));
console.log(0.035.toFixed(2));
console.log(0.045.toFixed(2));
console.log(new Number(200).toFixed(2));
console.log(new Number(200).toFixed(0));
console.log(new Number(1.23456789123456789).toFixed(5));

console.log("0.6".toFixed(0));
console.log("1.6".toFixed(0));
console.log("0.035".toFixed(2));
console.log("0.045".toFixed(2));
console.log("200".toFixed(2));
console.log("200".toFixed(0));
console.log("1.23456789123456789".toFixed(5));

function __toFixed(val) {
  const value = +this.toString();
  const decimal_number = this.toString().replace(/\d*\./, "").length;
  const denominator = 10 ** (decimal_number - val > 0 ? decimal_number -
    val : 0);
  let current_value = Math.round(value * 10 ** decimal_number / denominator) * denominator / 10 ** decimal_number + "";
  if (/\./.test(current_value)) {
    const current_decimal_number = current_value.replace(/\d*\./, "").length;
    if (current_decimal_number < val) {
      current_value += "0".repeat(val - current_decimal_number);
    }
  } else if (val) {
    current_value += "." + "0".repeat(val);
  }
  return current_value;
}
//zxx: 测试通过

@jsweber
Copy link

jsweber commented Jun 12, 2019

第一题:'1'
第二题: '2'
第二题: '0.04'
第四题: '0.04' /chrome;'0.05'/ie
第五题:
Number.prototype.toFixed = function(n){
    n = parseInt(n)
    if (n < 0) return NaN 

    let num = this.valueOf()
    let numStr = num +''
    let pointPos = numStr.indexOf('.')
    if (pointPos > -1){
        //有小数部分
        let decimal = numStr.slice(pointPos+1)
        if (decimal.length < n) {            
            //小数位需要填零
            let ret = numStr + (new Array(n - decimal.length).fill(0).join(''))

            return ret

        }else {
            //正常四舍五入
            //Math.round 对于负数也会有-4.5 =》 4 ; 4.5 =》 5
            let m = Math.pow(10, n)
            let flag = num > 0
            let t = parseInt(Math.round(Math.abs(num) * m)) / m
            t = flag ? t : -t
            return t+''
        }
    }else {
        //整数
        return num+ ( n > 0 ? '.' + (new Array(n).fill(0).join('')) : '')
    }

}

第六题:
String.prototype.toFixed = function(n){
    let num = +this
    if (isNaN(num)) return NaN
    return num.toFixed(n)
}


//测试
console.log('0.6'.toFixed(0)) //1
console.log('1.6'.toFixed(0))  //2
console.log('0.035'.toFixed(2)) //0.04
console.log('0.045'.toFixed(2)) //0.05
console.log('-0.045'.toFixed(2)) //-0.05
console.log(('-100.525').toFixed(5)) //-100.125
console.log('100.8123'.toFixed(0)) //101

@livetune
Copy link

livetune commented Jun 12, 2019

code pen demo

chrome 
1. '1'
2. '2'
3.  '0.04'
4.  '0.04'
5.
// 数字+1
function numAdd(str) {
    let flag = 1, newNum = ''
    for (let i = str.length - 1; i >= 0; i--) {
        if (str[i] === '.') {
            newNum = '.' + newNum
            continue
        }
        let res = (str[i] >> 0) + flag
        flag = res >= 10 ? 1 : 0;
        if (i === 0) {
            newNum = (str[i] === '-' ? '-' : '') + res + newNum
        } else {
            newNum = (flag ? '0' : res) + newNum
        }
    }
    return newNum
}
Number.prototype.toFixed = function (num) {
    let strNum = (this + '')
    const pointPos = strNum.indexOf('.')
    const strArr = strNum.split('.')

    // 如果保留有效数字的值比小数点后的数字大,补0
    if (num !== 0) {
        if (pointPos === -1) {
            strNum += '.'
        }
        strNum = strNum.padEnd((strNum.length - pointPos - 1) + num, '0')
    }
    // 小数再判断四舍五入
    if (pointPos !== -1) {
        if (strArr[1].length > num) {
            const newStr = strArr[0] + '.' + strArr[1].slice(0, num)
            return (strArr[1][num] >= 5 ? numAdd(newStr) : newStr).replace(/\.$/, '')
        } else {
            return strNum
        }
    }
    return strNum
}
console.log((0.6).toFixed(0),
    (1.6).toFixed(0),
   ( 0.035).toFixed(2),
    (0.045).toFixed(2))

6.
String.prototype.toFixed = function (num) {
            if (parseFloat(num) + '' === 'NaN') {
                return num
            } else {
                return Number.prototype.toFixed.call(this, num)
            }
       }

console.log('0.6'.toFixed(0),
    '1.6'.toFixed(0),
    '0.035'.toFixed(2),
    '0.045'.toFixed(2))

@tzmy
Copy link

tzmy commented Jun 13, 2019

demo

1. 1
2. 2
3. 0.04
4. 0.04

产生这种bug的原因网上说是丢失精度,不过我觉得有可能是此方法是采用了其它的舍入方法,类似于四舍六入五留双的方法,当然也没有完全符合这些规则。 参考

Number.prototype.toFixed = function (num) {
    return myFixed(this.valueOf(),num);
};
String.prototype.toFixed = function (num) {
    if(isNaN(this.valueOf())){
        return 'isNaN';
    }
    return myFixed(this.valueOf(),num);
};
function myFixed(number,num){
    var numberString = number.toString();
    var dotLength = 0;
    var fact = number;
    if(num<=0){
        return number;
    }
    var dot = numberString.split(".")[1]?numberString.split(".")[1]:'';
    var a = dot.slice(num,num+1);
    if(a!=''){
        if(parseInt(a)<5){

        }else{
            fact = Number(number) + Math.pow(10,-num);
        }
    }
    var factString = fact.toString();

    if(factString.split(".")[1]){
        fact = factString.split(".")[0]+'.' + factString.split(".")[1].slice(0,num);
    }else {
        fact = factString.split(".")[0]+'.';
    }
    fact = fact.toString();
    if(fact.split(".")[1]) {
        dotLength = dot.length;
    }
    for (var i=0;i<num-dotLength;i++){
        fact += '0';
    }
    return fact;
};

@XboxYan
Copy link

XboxYan commented Jun 13, 2019

Chrome Firefox IE
0.6.toFixed(0) 1 1 1
1.6.toFixed(0) 2 2 2
0.035.toFixed(2) 0.04 0.04 0.04
0.045.toFixed(2) 0.04 0.04 0.05

经测试,只有当保留小数位后面仅为5的时候会出现预期之外的结果,改为6就正常了,或者补一位小数

var NativetoFixed = Number.prototype.toFixed;//备份

//方式一:替换小数点保留精度后面一位5为6
var MytoFixed = function(digits){
    var reg = new RegExp('(\\d*.\\d{' + digits + '})5(\\d*)', 'g')
    var number = Number(this.toString().replace(reg, '$1' + 6 + '$2'));
    return NativetoFixed.call(number,digits);
}

//方式二:给小数点保留精度后面补一位小数
var MytoFixed = function(digits){
    var number = Number(this)+Math.pow(10,-digits-1);
    return NativetoFixed.call(number,digits);
}

Number.prototype.toFixed = MytoFixed;
String.prototype.toFixed = MytoFixed;

//有错误:1.0141  .toFixed = 1.0141 + 0.001 = 1.151 -> toFixed(2) = 1.15

demo

image

//zxx: 赞不一样的思路,富有创造力

@juzhiqiang
Copy link

  1. 1

  2. 2

  3. 0.04

  4. 0.04

  5. Number.prototype.toFixed = function(v){
    if(this === -Infinity) throw Error('-Infinity');
    if(this === Infinity) throw Error('Infinity');
    if(isNaN(this)) throw Error(this+'.toFixed is not a function')
    return parseInt(Math.round(this * Math.pow(10,v)))/Math.pow(10,v)
    }

  6. String.prototype.toFixed = function(v){
    var num = Number(this);
    if(!num) return 'Error Non-numeric string';
    return parseInt(Math.round(num * Math.pow(10,v)))/Math.pow(10,v)
    }

@CandyQiu
Copy link

CandyQiu commented Jun 13, 2019

我极其神奇的脑洞

// 1
document.write(0.6.toFixed(0));
document.write(" ");

// 2
document.write(1.6.toFixed(0));
document.write(" ");

// 3
document.write(0.035.toFixed(2));
document.write(" ");

// 4
document.write(0.045.toFixed(2));
document.write(" ");

// 5
Number.prototype.toFixed=function(v){
  //增加 nan判断   
  if(!window.isNaN(this)){
    let numberString = this.toString().split(".");
    let isAddOne = ( numberString[1].slice(v,v+1) - 0 ) >= 5;
    let result = "";
    let beforePonit =  numberString[0]+".";

    if(v===0){
       result =  ((beforePonit + numberString[1].slice(0,v)) - 0 ) +  (isAddOne ? 1 : 0) ;
    }
    else{
      let addNumber="";
      for(let i=0;i < v-1;i++){
        addNumber = addNumber +"0";
      }
      result =  ((beforePonit + numberString[1].slice(0,v)) - 0 ) +((beforePonit+  addNumber +  (isAddOne ? 1:0) ) - 0 ) ;
    }
    return result;
  }
}

// 6
String.prototype.toFixed = Number.prototype.toFixed;

@nichoasurey
Copy link

nichoasurey commented Jun 13, 2019

/*
*
 0.6.toFixed(0)
 "1"
 1.6.toFixed(0)
 "2"
 0.035.toFixed(0)
 "0"
 0.035.toFixed(2)
 "0.04"
 0.045.toFixed(2)
 "0.04"
*
* */
Number.prototype.oldToFixed = Number.prototype.toFixed;

Number.prototype.toFixed=function(a){
  let num = this - 0;
  a = a-0;
  if(!(a > 0 || a < 1)){
    a = 0
  }
  let times = Math.pow(10,a);
  let bigNum = num*times;
  let str = bigNum+'';
  if(str.indexOf('.')<0){
    return num.oldToFixed(a)
  }
  else{
    let numArr = str.split(".");
    let integer = numArr[0];
    let decimal = numArr[1];
    if(decimal>4){
      integer++
    }
    return integer/times
  }
}

String.prototype.toFixed=function(a){
  let num = this - 0;
  if(!(num > 0 || num < 1)){
    return num
  }
  return num.toFixed(a)
}

@Despair-lj
Copy link

Despair-lj commented Jun 13, 2019

Chrome Firefox Safari IE
0.6.toFixed(0) 1 1 1 1
1.6.toFixed(0) 2 2 2 2
0.035.toFixed(2) 0.04 0.04 0.04 0.04
0.045.toFixed(2) 0.04 0.04 0.04 0.05

第五/六问 demo

  • 一开始实现的方法,后面经过测试发现 toFixedFirstTime(1.015,2) 测试结果出错
function toFixedFirstTime(num, pre = 0) {
  num = parseFloat(num)
  // 非负数和取整
  pre = pre < 0 ? 0 : ~~pre
  // 计算四舍五入结果
  const result = Math.round(num * Math.pow(10, pre)) / Math.pow(10, pre) + ''
  // 将结果通过小数点分割成两个数字
  const strArr = result.split('.')
  // 如果存在小数点
  if (strArr[1] !== undefined) {
    const len = strArr[1].length
    // 如果小数部分长度不及提供的数值大
    if (len <= pre) {
      return `${result}`.padEnd(pre + strArr[0].length + 1, 0)
    } else {
      return `${result}`
    }
  }
  return (
    // 加一是因为小数点的长度
    `${result}` + (pre > 0 ? '.'.padEnd(pre + 1, 0) : '')
  )
}
  • 第二次重写 toFixed 函数, 测试 toFixedSecondTime(1.015,2) 成功
function toFixedSecondTime(num, pre = 0) {
  pre = pre < 0 ? 0 : ~~pre
  const strArr = `${num}`.split('.')
  const index = `${num}`.indexOf('.')

  if (strArr[1] !== undefined) {
    const len = strArr[1].length
    // 当指定数值比原数据的小数位长
    if (len <= pre) {
      return `${num}`.padEnd(pre + strArr[0].length + 1, 0)
    } else {
      // 最后一位数
      const last = strArr[1].slice(pre, pre + 1)
      // 小数点前数字长度
      const firstLength = strArr[0].length
      // 将源数据取出小数点变为一个整数
      let str = strArr.join('').split('')
      if (last >= 5) {
        let addFlag = false
        const res = str
          // 取出小数点前以及小数点后 pre 位的数值
          .slice(0, firstLength + pre)
          // 将数组反转
          .reverse()
          // 判断是否需要进位
          .reduce((res, acc, index) => {
            if (index === 0 || addFlag) {
              acc = parseInt(acc) + 1
              addFlag = false
            }
            if (acc === 10) {
              addFlag = true
              acc = 0
            }
            res.push(acc)
            return res
          }, [])
          // 将数组反转为原来的顺序
          .reverse()
          .join('')

        // 判断 pre 是否为 0, 为 0 则不添加小数点
        return (
          `${res.slice(0, index)}` +
          (pre > 0 ? `.${res.slice(index, index + pre)}` : '')
        )
      } else {
        // 判断 pre 是否为 0, 为 0 则不添加小数点
        return `${strArr[0]}` + (pre > 0 ? `.${strArr[1].substr(0, pre)}` : '')
      }
    }
  }
  return `${num}.`.padEnd(pre + `${num}`.length + 1, 0)
}
  • 第一个测试函数失败原因为 1.015 * Math.pow(10, 2) 返回结果是 101.49999999999999, Math.round 后返回 101。返回的结果比正常的结果小了 1e-14, 经过测试将这个数值先跟原始数值相加再进行 toFixed ,可以得到期待的值
function toFixedThirdTime(num, pre) {
   pre = pre < 0 ? 0 : ~~pre
   num = parseFloat(num)
   return (num + 1e-14).toFixed(pre)
}

Number.prototype.toMyFixed = String.prototype.toMyFixed = function(pre) {
   pre = pre < 0 ? 0 : ~~pre
   num = parseFloat(this)
   return (num + 1e-14).toFixed(pre)
}
//zxx: 研究精神不错,恕我代码看得眼花,可读性这块并不算友好

@sghweb
Copy link

sghweb commented Jun 13, 2019

弄了个turbo做测试

ie8 ie9 ie10 edge chrome firefox opera
0.6.toFixed(0) 0 1 1 1 1 1 1
1.6.toFixed(0) 2 2 2 2 2 2 2
0.035.toFixed(2) 0.04 0.04 0.04 0.04 0.04 0.04 0.04
0.045.toFixed(2) 0.05 0.05 0.05 0.05 0.04 0.04 0.04

参考简书上的一篇文章做了小调整

// 第五题
Number.prototype.toFixed = function(n) {
  const number = this;
  if (isNaN(number)) {
    return number.toString();
  }
  if (typeof(n) == 'undefined' || n == 0) {
    return (Math.round(number)).toString();
  }
  let result = number.toString();
  const arr = result.split('.');
  // 整数的情况
  if (arr.length < 2) {
    result += '.';
    for (let i = 0; i < n; i += 1) {
      result += '0';
    }
    return result;
  }
  const integer = arr[0];
  const decimal = arr[1];
  if (decimal.length == n) {
    return result;
  }
  if (decimal.length < n) {
    for (let i = 0; i < n - decimal.length; i += 1) {
      result += '0';
    }
    return result;
  }
  result = integer + '.' + decimal.substr(0, n);
  const last = decimal.substr(n, 1);
  // 四舍五入,转换为整数再处理,避免浮点数精度的损失
  if (parseInt(last, 10) >= 5&&result>0) {
    const x = Math.pow(10, n);
    result = (Math.round((parseFloat(result) * x)) + 1) / x;
    result = result.toFixed(n);
  }else if(parseInt(last, 10) >= 5&&result<0){
    const x = Math.pow(10, n);
    result = (Math.round((parseFloat(result) * x)) - 1) / x;
    result = result.toFixed(n);
  }
  return result;
};
// 第六题
String.prototype.toFixed=Number.prototype.toFixed

@asyncguo
Copy link

1. "1"
2. "2"
3. "0.04"
4. "0.04"

5-6. 下面的方法完全以字符串的方式进行处理

function toFixedFn (digits) {
  if (digits < 0) throw new Error("位数不得小于0");

  let [intNum, floatNum] = this.toString().split(".");
  // 若无小数部位,直接用原生toFixed
  if (floatNum === undefined) return parseInt(intNum).toFixed(digits);
  // 若 digits 为0,直接利用round取整
  if (digits === 0) return Math.round(this)
  // 若小数部位长度少于digits,进行补位
  if (floatNum.length < digits) {
    let diff = digits - floatNum.length
    floatNum = parseInt(floatNum) * Math.pow(10, diff)
  }
  // 小数部位模拟取整
  let reg = new RegExp(`(.{${digits}})`);
  floatNum = Math.round(floatNum.toString().replace(reg, "$1.").slice(0, digits + 2))
  floatNum = (floatNum / Math.pow(10, digits)).toString().slice(1)
  // 处理完后,进行补位
  if (floatNum.length - 1 < digits) {
    floatNum = floatNum + '0'.repeat(digits - floatNum.length + 1)
  }
  return intNum + floatNum
};

Number.prototype.toFixedNum = toFixedFn
String.prototype.toFixedNum = toFixedFn

// 测试
console.log((0.6).toFixedNum(0));   
console.log((1.6).toFixedNum(0));   
console.log((0.035).toFixedNum(2));
console.log((0.045).toFixedNum(2));
console.log((0.2).toFixedNum(6));  

console.log('0.6'.toFixedNum(0));   
console.log('1.6'.toFixedNum(0));   
console.log('0.035'.toFixedNum(2));
console.log('0.045'.toFixedNum(2));
console.log('0.2'.toFixedNum(6));  
// 输出
1
2
"0.04"
"0.05"
"0.200000"

@lifelikejuly
Copy link

  • 使用Node
    result:
    console.log(0.6.toFixed(0)) => 1
    console.log(1.6.toFixed(0)) => 2
    console.log(0.035.toFixed(2)) => 0.04
    console.log(0.045.toFixed(2)) => 0.04
  • chrome浏览器
    result:
    console.log(0.6.toFixed(0)) => 1
    console.log(1.6.toFixed(0)) => 2
    console.log(0.035.toFixed(2)) => 0.04
    console.log(0.045.toFixed(2)) => 0.04

第五题:

Number.prototype.toFixed = function(n) {
    var power = Math.pow(10, n);
    var value = this * power + 0.5;
    value = parseInt(value, 10) / power;
    return value;
};

第六题:

String.prototype.toFixed = function(n) {
    var num = Number(this);
    if(!num) return NaN;
    var power = Math.pow(10, n);
    var value = num * power + 0.5;
    value = parseInt(value, 10) / power;
    return value;
};

@frankyeyq
Copy link

frankyeyq commented Jun 14, 2019

chrome 
1. 1
2. 2
3. 0.04
4. 0.04
5.
let oldtoFixed = Number.prototype.toFixed
Number.prototype.toFixed = function(n) {
    return oldtoFixed.call(Number.parseFloat(this+''+1), n)
}

// 有错误 new Number(10).toFixed(2) "101.00"
6. String.prototype.toFixed = Number.prototype.toFixed

@zhangxinxu
Copy link
Owner Author

zhangxinxu commented Jun 15, 2019

本期要点:

  1. toFixed有两个问题,一是兼容性,二是四舍五入不符合正常的四舍五入认知。金钱计算的时候容易出问题,必须两位小数。
  2. 应该返回字符串;补全末尾的0。
  3. 机智是实现:方式一:替换小数点保留精度后面一位5为6,方式二:给小数点保留精度后面补一位小数。其中方式2是最简单的,XboxYan 和 frankyeyq 实现都有bug,下面是调整后的实现。
var oldtoFixed = Number.prototype.toFixed
Number.prototype.toFixed = function(digits){
    var length = (parseFloat(this) + '').replace(/^\d+\.?/, '').length;
    var len = length > digits ? length : digits;
    var number = Number(this) + Math.pow(10, -len-1);
    return oldtoFixed.call(number, digits);
}

@Despair-lj
Copy link

var oldtoFixed = Number.prototype.toFixed
Number.prototype.toFixed = function(digits){
    var length = (parseFloat(this) + '').replace(/^\d+\.?/, '').length;
    var number = Number(this) + Math.pow(10, -length-1);
    return oldtoFixed.call(number, digits);
}

length 判断存在问题, 如果是 10.toFixed(2) 会在小数点后一位添加 1 导致返回 10.10, 所以应该判断 length 和 digits 的大小

var oldtoFixed = Number.prototype.toFixed
Number.prototype.toMyFixed = function(digits){
    var length = (parseFloat(this) + '').replace(/^\d+\.?/, '').length;
    var len = length > digits ? length : digits
    var number = Number(this) + Math.pow(10, -len-1);
    return oldtoFixed.call(number, digits);
}

@zhangxinxu
Copy link
Owner Author

var oldtoFixed = Number.prototype.toFixed
Number.prototype.toFixed = function(digits){
    var length = (parseFloat(this) + '').replace(/^\d+\.?/, '').length;
    var number = Number(this) + Math.pow(10, -length-1);
    return oldtoFixed.call(number, digits);
}

length 判断存在问题, 如果是 10.toFixed(2) 会在小数点后一位添加 1 导致返回 10.10, 所以应该判断 length 和 digits 的大小

var oldtoFixed = Number.prototype.toFixed
Number.prototype.toMyFixed = function(digits){
    var length = (parseFloat(this) + '').replace(/^\d+\.?/, '').length;
    var len = length > digits ? length : digits
    var number = Number(this) + Math.pow(10, -len-1);
    return oldtoFixed.call(number, digits);
}

多谢反馈~

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests