javascriptfloating-pointprecisiontofixed

Overwrite toFixed() with appropriate replacement to fix floating point error javascript


This is my attempt to fix the JavaScript toFixed() function...

Any input, ideas, corrections for possible errors are much appreciated!

This is my attempt -> Demo below (see console log)

enter image description here

Number.prototype.toFixed = function(fractionDigits) {
    var digits = parseInt(fractionDigits) || 0;
    var num = Number(this);
    if( isNaN(num) ) {
        return 'NaN';
    }
    
    var sign = num < 0 ? -1 : 1;
    if (sign < 0) { num = -num; }
    digits = Math.pow(10, digits);
    num *= digits;
    num = Math.round( Math.round(num * Math.pow(10,12)) / Math.pow(10,12) );
    var finalNumber = sign * num / digits;

    // add 0 after last decimal number (not 0) for as many as requested (fractionDigits)
    // in else case, check if requested digits exceed actual, then add 0 (avoid 10.1 for toFixed(2))

    if(fractionDigits > 0 && finalNumber.toString().indexOf('.') == -1){
        // check that .00 is present
        finalNumber = finalNumber.toString() + '.' + '0'.repeat(fractionDigits);
    } else if(fractionDigits > finalNumber.toString().split('.')[1]?.length){
        finalNumber = finalNumber.toString() + '0'.repeat((fractionDigits - finalNumber.toString().split('.')[1]?.length));
    }
    
    return finalNumber.toString(); // tofixed returns as string always, do the same
}

console.log('(35.355).toFixed(2)', (35.355).toFixed(2));
console.log('(35.1).toFixed(2)', (35.1).toFixed(2));
console.log('(35).toFixed(2)', (35).toFixed(2));

Number.prototype.toFixed = function(fractionDigits) {
//function toFixed(numberInput, fractionDigits){
    var digits = parseInt(fractionDigits) || 0;
    var num = Number(this);
    if( isNaN(num) ) {
        return 'NaN';
    }
    
    var sign = num < 0 ? -1 : 1;
    if (sign < 0) { num = -num; }
    digits = Math.pow(10, digits);
    num *= digits;
    num = Math.round( Math.round(num * Math.pow(10,12)) / Math.pow(10,12) );
    var finalNumber = sign * num / digits;

    // add 0 after last decimal number (not 0) for as many as requested (fractionDigits)

    if(fractionDigits > 0 && finalNumber.toString().indexOf('.') == -1){
        // check that .00 is present
        finalNumber = finalNumber.toString() + '.' + '0'.repeat(fractionDigits);
    } else if(fractionDigits > finalNumber.toString().split('.')[1]?.length){
        finalNumber = finalNumber.toString() + '0'.repeat((fractionDigits - finalNumber.toString().split('.')[1]?.length));
    }
    
    return finalNumber.toString(); // tofixed returns as string always, do the same
}

console.log('post-fix | (35.355).toFixed(2)', (35.355).toFixed(2));
console.log('post-fix | (35.1).toFixed(2)', (35.1).toFixed(2));
console.log('post-fix | (35).toFixed(2)', (35).toFixed(2));


Solution

  • If it were me, I might have this string manipulation approach:

    Number.prototype.toFixed = function(fractionDigits) {
      var number = String(this);
      var digits = fractionDigits || 0, length;
      
      if(digits < 0 && digits > 100) 
        throw 'RangeError: toFixed() digits argument must be between 0 and 100';
    
      var decimal = number.match(/(?<=\.)(\d*)/g);
      var factor = Math.pow(10, digits);
      if (decimal && decimal[0].length >= digits) 
        return String(Math.round(Number(number + '1') * factor) / factor);
      else {
        var length = digits - (decimal ? decimal[0].length : 0);
        var delimiter = number.includes('.') || !length ? '' : '.';
        return String(number) + delimiter + '0'.repeat(length);
      }
    }
    
    function test() {
      console.log((-35.555).toFixed(2))
      console.log((-35.35).toFixed(2))
      console.log((-35.9).toFixed(2))
      console.log((-35).toFixed(2))
    }
    

    Note:

    Output:

    output