简体   繁体   English

具有 3 位小数的 toFixed(2) 的可靠 JS 舍入数

[英]Reliable JS rounding numbers with toFixed(2) of a 3 decimal number

I am simply trying to round up 1.275.toFixed(2) and I was expecting a return of 1.28 , rather than 1.27.我只是想汇总1.275.toFixed(2)并且我期望返回1.28 ,而不是 1.27。

Using various calculators and the simple method of rounding to the nearest hundredth, if the last digit is greater than or equal to five, it should round up.使用各种计算器和四舍五入到最接近的百分位的简单方法,如果最后一位大于或等于五,则应四舍五入。

If this doesn't work with toFixed(2), how would it?如果这不适用于 toFixed(2),它会怎样?

People asking whether console.log(1.275.toFixed(2)) prints off 1.28, here's a quick screenshot MacOS Chrome Version 55.0.2883.95 (64-bit)人们询问 console.log(1.275.toFixed(2)) 是否打印出 1.28,这是 MacOS Chrome 版本 55.0.2883.95(64 位)的快速截图

在此处输入图像描述

The 1.275 base 10 number has finite digits but becomes periodic when converted to base 2: 1.275 10 为基数的数字是有限的,但在转换为以 2 为基数时变为周期性:

= 0b1.01000110011001100110011001100110011001100110011010
         ^^^^

Since it has infinite digits, it cannot be represented exactly in a computer unless you use an arbitrary precision library (a library than represents numbers as text to keep them in base 10).由于它具有无限位数,因此除非您使用任意精度库(一个库将数字表示为文本以将它们保持在基数为 10),否则无法在计算机中精确表示。 JavaScript numbers do not use such library for performance reasons.出于性能原因,JavaScript 数字不使用此类库。

Since the original value has already lost precision when it reaches JavaScript, rounding it will not improve that.由于原始值在到达 JavaScript 时已经失去精度,因此四舍五入不会改善它。

The toFixed() method is unreliable in its rounding (see Álvaro González' answer as to why this is the case). toFixed()方法在其舍入方面不可靠(请参阅Álvaro González 的回答,了解为什么会出现这种情况)。

In both current Chrome and Firefox, calling toFixed() yields the following inconsistent results:在当前的 Chrome 和 Firefox 中,调用toFixed()产生以下不一致的结果:

35.655.toFixed(2) // Yields "36.66" (correct)
35.855.toFixed(2) // Yields "35.85" (wrong, should be "35.86")

MDN describes a reliable rounding implementation : MDN 描述了一个可靠的舍入实现

// Closure
(function() {
  /**
   * Decimal adjustment of a number.
   *
   * @param {String}  type  The type of adjustment.
   * @param {Number}  value The number.
   * @param {Integer} exp   The exponent (the 10 logarithm of the adjustment base).
   * @returns {Number} The adjusted value.
   */
  function decimalAdjust(type, value, exp) {
    // If the exp is undefined or zero...
    if (typeof exp === 'undefined' || +exp === 0) {
      return Math[type](value);
    }
    value = +value;
    exp = +exp;
    // If the value is not a number or the exp is not an integer...
    if (isNaN(value) || !(typeof exp === 'number' && exp % 1 === 0)) {
      return NaN;
    }
    // Shift
    value = value.toString().split('e');
    value = Math[type](+(value[0] + 'e' + (value[1] ? (+value[1] - exp) : -exp)));
    // Shift back
    value = value.toString().split('e');
    return +(value[0] + 'e' + (value[1] ? (+value[1] + exp) : exp));
  }

  // Decimal round
  if (!Math.round10) {
    Math.round10 = function(value, exp) {
      return decimalAdjust('round', value, exp);
    };
  }
  // Decimal floor
  if (!Math.floor10) {
    Math.floor10 = function(value, exp) {
      return decimalAdjust('floor', value, exp);
    };
  }
  // Decimal ceil
  if (!Math.ceil10) {
    Math.ceil10 = function(value, exp) {
      return decimalAdjust('ceil', value, exp);
    };
  }
})();

// Round
Math.round10(55.55, -1);   // 55.6
Math.round10(55.549, -1);  // 55.5
Math.round10(55, 1);       // 60
Math.round10(54.9, 1);     // 50
Math.round10(-55.55, -1);  // -55.5
Math.round10(-55.551, -1); // -55.6
Math.round10(-55, 1);      // -50
Math.round10(-55.1, 1);    // -60
Math.round10(1.005, -2);   // 1.01 -- compare this with Math.round(1.005*100)/100 above
// Floor
Math.floor10(55.59, -1);   // 55.5
Math.floor10(59, 1);       // 50
Math.floor10(-55.51, -1);  // -55.6
Math.floor10(-51, 1);      // -60
// Ceil
Math.ceil10(55.51, -1);    // 55.6
Math.ceil10(51, 1);        // 60
Math.ceil10(-55.59, -1);   // -55.5
Math.ceil10(-59, 1);       // -50

While I am a little late, this could help someone with the same requirement.虽然我有点晚了,但这可以帮助有相同要求的人。 If the value is a string, simply add an additional "1" to the end and your issue will be fixed.如果该值是一个字符串,只需在末尾添加一个额外的“1”,您的问题就会得到解决。 If input = 10.55 then it would become 10.551 which in turn would become 10.56 .如果input = 10.55那么它将变成10.551又变成10.56

This example uses jQuery此示例使用 jQuery

function toTwoDecimalPlaces(input) {
        var value = $(input).val();
        if (value != null) {
            value = parseFloat(value + "1").toFixed(2);
        }
        $(input).val(value);
}

Update: If the input is accepting whole numbers and/or numbers with 1 decimal place, then you will want to check how many decimal places have been used.更新:如果输入接受整数和/或带有 1 个小数位的数字,那么您将需要检查使用了多少个小数位。 If it is greater than the fixed amount then add the "1".如果它大于固定数量,则添加“1”。

function toTwoDecimalPlaces(input) {
        var value = $(input).val();
        if (value.includes(".")) {
            var splitValue = value.split(".");
            if (splitValue[1].length > 2) {
                value = parseFloat(value + "1").toFixed(2);
            }
        }
        $(input).val(value);
}

According to Robby's answer, MDN describes a reliable rounding implementation , therefore I stripped it down to the following snippet to solve my issue of rounding a 3 decimal number of 1.275 to 1.28 .根据 Robby 的回答,MDN 描述了一个可靠的舍入实现,因此我将其分解为以下代码段以解决将1.275的 3 个十进制数四舍五入到1.28 Tested in FF4, Chrome 55 and Safari 10.0.3 on MacOS在 MacOS 上的 FF4、Chrome 55 和 Safari 10.0.3 中测试

function decimalAdjust(c,a,b){if("undefined"===typeof b||0===+b)return Math[c](a);a=+a;b=+b;if(isNaN(a)||"number"!==typeof b||0!==b%1)return NaN;a=a.toString().split("e");a=Math[c](+(a[0]+"e"+(a[1]?+a[1]-b:-b)));a=a.toString().split("e");return+(a[0]+"e"+(a[1]?+a[1]+b:b))}Math.round10||(Math.round10=function(c,a){return decimalAdjust("round",c,a)});

Math.round10(1.275, -2);

Probably one of the simplest solutions, and one I always use.可能是最简单的解决方案之一,也是我经常使用的解决方案。 Any number in the {} will truncate to that many decimals, my case below to the second decimal {2} . {}中的任何数字都将截断为那么多小数,下面我的例子是第二个小数{2}

let Num = 23.49876
let NumString = Num+''
let toSecondDecimalPlace = NumString.replace(/(.*\.\d{2})(.+)/,'$1'); 
console.log('to Second Decimal Place:', toSecondDecimalPlace) // 23.49

let backToNumber = Number(toSecondDecimalPlace)
console.log('backToNumber:', backToNumber) // 23.49

Using the Custom Function to Round a Number To2 Decimal Places in JavaScript.在 JavaScript 中使用自定义函数将数字四舍五入到 2 个小数位。

function roundToTwo(num) {
    return +(Math.round(num + "e+2")  + "e-2");
}

console.log(roundToTwo(2.005));

For more information visit https://www.delftstack.com/howto/javascript/javascript-round-to-2-decimal-places/有关更多信息,请访问https://www.delftstack.com/howto/javascript/javascript-round-to-2-decimal-places/

Face it.面对它。 If you want a specific rule for what happens when the number is exactly "one-half" in the last place, you should write your own rounding routine.如果你想要一个特定的规则来说明当数字在最后一位恰好是“二分之一”时会发生什么,你应该编写你自己的舍入例程。

Alas ECMA decided to ignore IEEE 754 and invent yet another rounding algorithm.唉,ECMA 决定忽略 IEEE 754 并发明另一种舍入算法。 754 defaults to "round to nearest even" -- 3.5 -> 4 and 4.5 -> 4. This, of course, disagrees with "banking" rules. 754 默认为“舍入到最接近的偶数”——3.5 -> 4 和 4.5 -> 4。当然,这不符合“银行”规则。

The OP has not said what he actually wants for other cases. OP 没有说出他对其他案件的实际要求。 Their screenshot seems to imply that JavaScript is converting 1.275 to a binary floating point approximation.他们的屏幕截图似乎暗示 JavaScript 正在将 1.275 转换为二进制浮点数近似值。 (Alvaro provides the details.) (阿尔瓦罗提供了细节。)

A different JS implementation might store 1.275 in decimal , thereby making things work differently.不同的 JS 实现可能会以decimal形式存储 1.275 ,从而使事情变得不同。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM