2014-01-13 3 views
13

Ожидаемый результат:Javascript toFixed

(1.175).toFixed(2) = 1.18 and 
(5.175).toFixed(2) = 5.18 

Но в JS показа:

(1.175).toFixed(2) = 1.18 but 
*(5.175).toFixed(2) = 5.17* 

Как исправить эту проблему?

+0

Почему это проблема? –

+0

Я не знаю, разрешила ли эта проблема ваша проблема: http://stackoverflow.com/questions/10808671/javascript-how-to-prevent-tofixed-from-rounding-off-decimal-numbers –

+0

Каков ожидаемый результат (5.175). ToFixed (2) –

ответ

6

Вы всегда можете попробовать использовать круглые, вместо toFixed.

Math.round(5.175*100)/100

Можно даже попробовать положить его в какой-то метод прототипа, если вы хотите.

Создано jsBin, в котором используется простой прототип для номера.

Number.prototype.toFixed = function(decimals) { 
return Math.round(this * Math.pow(10, decimals))/(Math.pow(10, decimals)); 
}; 
+1

Мне нравится это решение, за исключением того, что вы побеждаете цель функции, которая заключается в том, чтобы обеспечить определенное количество цифр после знаков после запятой. Ваша функция не удалась с цифрами 1, 1.0, 1.00000 и т. Д. Вы можете решить эту проблему, просто добавив «toFixed()» в оператор return, например: «return (Math.round (this * Math.pow (10, десятичные знаки)))/(Math.pow (10, десятичные числа))). ToFixed (decimals); " – stldoug

+0

Мне нравится ваш комментарий, так как я имел дело с той же проблемой, когда я использовал это решение. Проблема с вашим подходом заключается в том, что вы не можете использовать функцию, которую вы переопределяете в определении этой функции. Он переходит в рекурсивную землю лалы. Мое решение здесь https://jsfiddle.net/yxjd78sj/ фиксирует старое определение функции .toFixed() и использует его как .oldToFixed(). Дайте мне знать, что вы думаете! – 3abqari

11

Это не ошибка. Это связано с тем, что цифры не сохраняются в десятичной форме, но в IEEE754 (так 5.175 не точно).

Если вы хотите, чтобы округлить в определенном направлении (вверх) и вы постоянно имеют номера этой точности, вы можете использовать этот трюк:

(5.175 + 0.00001).toFixed(2) 
2

Это потому, что номера хранятся как IEEE754.

Я бы рекомендовал вам использовать класс Math (круглые, напольные или потолочные методы, в зависимости от ваших потребностей).

Я только что создал класс MathHelper, который может легко решить вашу проблему:

var MathHelper = (function() { 
    this.round = function (number, numberOfDecimals) { 
     var aux = Math.pow(10, numberOfDecimals); 
     return Math.round(number * aux)/aux; 
    }; 
    this.floor = function (number, numberOfDecimals) { 
     var aux = Math.pow(10, numberOfDecimals); 
     return Math.floor(number * aux)/aux; 
    }; 
    this.ceil = function (number, numberOfDecimals) { 
     var aux = Math.pow(10, numberOfDecimals); 
     return Math.ceil(number * aux)/aux; 
    }; 

    return { 
     round: round, 
     floor: floor, 
     ceil: ceil 
    } 
})(); 

Использование:

MathHelper.round(5.175, 2) 

Демо: http://jsfiddle.net/v2Dj7/

0

На самом деле я думаю, что это является ошибка в реализации Number.prototype.toFixed. Алгоритм, приведенный в ECMA-262 20.1.3.3 10-a говорит, чтобы округлить в качестве тай-брейкера. Как отмечали другие, вероятно, не существует ничьей связи из-за неточности с плавающей запятой в реализации. Но это не делает это правильно.

По крайней мере, такое поведение согласовано между FF, Chrome, Opera, Safari. (Не пробовали другие.)

FWIW, вы действительно можете реализовать свою собственную версию toFixed за спецификацию в JS, и это ведет себя так, как вы ожидали. См. http://jsfiddle.net/joenudell/7qahrb6d/.

0

Kippie ваше решение имеет проблемы один из них

39133.005.toFixed(2) => 39133 

var Calc = function() { 
    var self = this; 

this.float2Array = function(num) { 
    var floatString = num.toString(), 
     exp = floatString.indexOf(".") - (floatString.length - 1), 
     mant = floatString.replace(".", "").split("").map(function (i) { return parseInt(i); }); 
    return { exp: exp, mant: mant }; 
}; 

this.round2 = function (num, dec, sep) { 
    var decimal = !!dec ? dec : 2, 
    separator = !!sep ? sep : '', 
    r = parseFloat(num), 
    exp10 = Math.pow(10, decimal); 
    r = Math.round(r * exp10)/exp10; 

    var rr = Number(r).toFixed(decimal).toString().split('.'); 

    var b = rr[0].replace(/(\d{1,3}(?=(\d{3})+(?:\.\d|\b)))/g, "\$1" + separator); 
    r = (rr[1] ? b + '.' + rr[1] : b); 

    return r; 
}; 

this.toFixed10 = function (f, num) { 
    var prepareInt = self.float2Array(f), 
     naturalInt = prepareInt.mant, 
     places = Math.abs(prepareInt.exp), 
     result = prepareInt.mant.slice(), 
     resultFixedLenth; 

    // if number non fractional or has zero fractional part 
    if (f.isInt()) { 
     return f.toFixed(num); 
    } 
    // if the number of decimal places equals to required rounding 
    if (places === num) { 
     return Number(f).toString(); 
    } 
    //if number has trailing zero (when casting to string type float 1.0050 => "1.005" => 005 <0050) 
    if (places < num) { 
     return Number(f).round2(num); 
    } 

    for (var e = naturalInt.length - (places > num ? (places - num) : 0), s = 0; e >= s; e--) { 
     if (naturalInt.hasOwnProperty(e)) { 
      if (naturalInt[e] > 4 && naturalInt[e - 1] < 9) { 
       result[e] = 0; 
       result[e - 1] = naturalInt[e - 1] + 1; 
       break; 
      } else if (naturalInt[e] > 4 && naturalInt[e - 1] === 9) { 
       result[e] = 0; 
       result[e - 1] = 0; 
       result[e - 2] = naturalInt[e - 2] < 9 ? naturalInt[e - 2] + 1 : 0; 
      } else if (e === 0 && naturalInt[e] === 9 && naturalInt[e + 1] === 9) { 
       result[e] = 0; 
       result.unshift(1); 
      } else { 
       break; 
      } 
     } 
    } 

    if (places - num > 0) { 
     resultFixedLenth = result.slice(0, -(places - num)); 
    } else { 
     for (var i = 0, l = num - places; i < l; i++) { 
      result.push(0); 
     } 
     resultFixedLenth = result; 
    } 

    return (parseInt(resultFixedLenth.join(""))/Math.pow(10, num)).round2(num); 
}; 
this.polyfill = function() { 
    if (!Array.prototype.map) { 
     Array.prototype.map = function (callback, thisArg) { 
      var T, A, k; 
      if (this == null) { throw new TypeError(' this is null or not defined'); } 
      var O = Object(this), len = O.length >>> 0; 
      if (typeof callback !== 'function') { throw new TypeError(callback + ' is not a function'); } 
      if (arguments.length > 1) { T = thisArg; } 

      A = new Array(len); 
      k = 0; 
      while (k < len) { 
       var kValue, mappedValue; 
       if (k in O) { 
        kValue = O[k]; 
        mappedValue = callback.call(T, kValue, k, O); 
        A[k] = mappedValue; 
       } 
       k++; 
      } 
      return A; 
     }; 
    } 
}; 

this.init = function() { 
    self.polyfill(); 
    Number.prototype.toFixed10 = function (decimal) { 
     return calc.toFixed10(this, decimal); 
    } 
    Number.prototype.round2 = function (decimal) { 
     return calc.round2(this, decimal); 
    } 
    Number.prototype.isInt = function() { 
     return (Math.round(this) == this); 
    } 
} 
}, calc = new Calc(); calc.init(); 

это работает хорошо)

0
obj = { 
    round(val) { 
     const delta = 0.00001 
     let num = val 
     if (num - Math.floor(num) === 0.5) { 
     num += delta 
     } 
     return Math.round(num + delta) 
    }, 
    fixed(val, count = 0) { 
     const power = Math.pow(10, count) 
     let res = this.round(val * power)/power 
     let arr = `${res}`.split('.') 
     let addZero = '' 
     for (let i = 0; i < count; i++) { 
     addZero += '0' 
     } 
     if (count > 0) { 
     arr[1] = ((arr[1] || '') + addZero).substr(0, count) 
     } 
     return arr.join('.') 
    } 
} 
obj.fixed(5.175, 2) 

// "5,18"