Pregunta Redondee a un máximo de 2 decimales (solo si es necesario)


Me gustaría redondear al menos 2 decimales, pero solo si es necesario.

Entrada:

10
1.7777777
9.1

Salida:

10
1.78
9.1

¿Cómo puedo hacer esto en JavaScript?


1835
2017-08-06 17:17


origen


Respuestas:


Utilizar Math.round(num * 100) / 100


2280
2017-08-06 17:20



Si el valor es un tipo de texto:

parseFloat("123.456").toFixed(2);

Si el valor es un número:

var numb = 123.23454;
numb = numb.toFixed(2);

Hay una desventaja de que valores como 1.5 darán "1.50" como salida. Una solución sugerida por @minitech:

var numb = 1.5;
numb = +numb.toFixed(2);
// Note the plus sign that drops any "extra" zeroes at the end.
// It changes the result (which is a string) into a number again (think "0 + foo"),
// which means that it uses only as many digits as necessary.

Parece que Math.round es una mejor solución. ¡Pero no lo es! En algunos casos lo hará NO redondear correctamente:

Math.round(1.005 * 1000)/1000 // Returns 1 instead of expected 1.01!

toFixed () también lo hará NO redondo correctamente en algunos casos (probado en Chrome v.55.0.2883.87)!

Ejemplos:

parseFloat("1.555").toFixed(2); // Returns 1.55 instead of 1.56.
parseFloat("1.5550").toFixed(2); // Returns 1.55 instead of 1.56.
// However, it will return correct result if you round 1.5551.
parseFloat("1.5551").toFixed(2); // Returns 1.56 as expected.

1.3555.toFixed(3) // Returns 1.355 instead of expected 1.356.
// However, it will return correct result if you round 1.35551.
1.35551.toFixed(2); // Returns 1.36 as expected.

Supongo que esto es porque 1.555 es en realidad algo así como flotar 1.55499994 detrás de escena.

Solución 1 es usar un script con el algoritmo de redondeo requerido, por ejemplo:

function roundNumber(num, scale) {
  if(!("" + num).includes("e")) {
    return +(Math.round(num + "e+" + scale)  + "e-" + scale);
  } else {
    var arr = ("" + num).split("e");
    var sig = ""
    if(+arr[1] + scale > 0) {
      sig = "+";
    }
    return +(Math.round(+arr[0] + "e" + sig + (+arr[1] + scale)) + "e-" + scale);
  }
}

https://plnkr.co/edit/uau8BlS1cqbvWPCHJeOy?p=preview

Solución 2 es evitar cálculos iniciales y extraer valores redondeados del servidor back-end.


2319
2017-10-11 00:27



Puedes usar

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

Encontré esto de nuevo MDN. Su camino evita el problema con 1.005 que era mencionado.

roundToTwo(1.005)
1.01
roundToTwo(10)
10
roundToTwo(1.7777777)
1.78
roundToTwo(9.1)
9.1
roundToTwo(1234.5678)
1234.57

323
2017-08-21 12:56



La respuesta de MarkG es la correcta. Aquí hay una extensión genérica para cualquier número de decimales.

Number.prototype.round = function(places) {
  return +(Math.round(this + "e+" + places)  + "e-" + places);
}

Uso:

var n = 1.7777;    
n.round(2); // 1.78

Prueba de unidad:

it.only('should round floats to 2 places', function() {

  var cases = [
    { n: 10,      e: 10,    p:2 },
    { n: 1.7777,  e: 1.78,  p:2 },
    { n: 1.005,   e: 1.01,  p:2 },
    { n: 1.005,   e: 1,     p:0 },
    { n: 1.77777, e: 1.8,   p:1 }
  ]

  cases.forEach(function(testCase) {
    var r = testCase.n.round(testCase.p);
    assert.equal(r, testCase.e, 'didn\'t get right number');
  });
})

115
2017-11-01 07:40



Uno puede usar .toFixed(NumberOfDecimalPlaces).

var str = 10.234.toFixed(2); // => '10.23'
var number = Number(str); // => 10.23

69
2017-10-21 17:02



Un método de redondeo preciso. Fuente: Mozilla

(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);
        };
    }
})();

Ejemplos:

// 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

54
2017-08-01 08:02



Ninguna de las respuestas encontradas aquí es correcta. @stinkycheeseman pidió a redondeo, redondearon el número.

Para redondear, usa esto:

Math.ceil(num * 100)/100;

53
2018-06-13 09:35



Esta pregunta es complicada

Supongamos que tenemos una función, roundTo2DP(num), que toma un flotante como argumento y devuelve un valor redondeado a 2 decimales. ¿Qué debería evaluar cada una de estas expresiones?

  • roundTo2DP(0.014999999999999999)
  • roundTo2DP(0.0150000000000000001)
  • roundTo2DP(0.015)

La respuesta "obvia" es que el primer ejemplo debería redondear a 0.01 (porque está más cerca de 0.01 que a 0.02) mientras que los otros dos deberían redondear a 0.02 (porque 0.0150000000000000001 está más cerca de 0.02 que de 0.01, y porque 0.015 está exactamente a medio camino entre ellos y hay una convención matemática que dichos números se redondean).

El truco, que puedes haber adivinado, es que roundTo2DP  no puede posiblemente implementarse para dar esas respuestas obvias, porque los tres números que se le pasan son el mismo numero. Los números binarios flotantes IEEE 754 (del tipo utilizado por JavaScript) no pueden representar exactamente la mayoría de los números no enteros, por lo que los tres literales numéricos anteriores se redondean a un número de punto flotante válido cercano. Este número, como sucede, es exactamente

0.01499999999999999944488848768742172978818416595458984375

que está más cerca de 0.01 que de 0.02.

Puede ver que los tres números son los mismos en la consola del navegador, en el shell del nodo u otro intérprete de JavaScript. Simplemente compárelos:

> 0.014999999999999999 === 0.0150000000000000001
true

Entonces cuando escribo m = 0.0150000000000000001, el valor exacto de mcon el que termino está más cerca de 0.01 de lo que es 0.02. Y sin embargo, si yo convierto m a una cadena ...

> var m = 0.0150000000000000001;
> console.log (String (m));
0.015
> var m = 0.014999999999999999;
> console.log (String (m));
0.015

... obtengo 0.015, que debería redondear a 0.02, y que es notablemente no el número del lugar con 56 decimales que dije antes que todos estos números eran exactamente iguales a. Entonces, ¿qué magia oscura es esta?

La respuesta se puede encontrar en la especificación ECMAScript, en la sección 7.1.12.1: ToString aplicado al tipo de número. Aquí las reglas para convertir un número metro a una cuerda se establecen. La parte clave es el punto 5, en el que un entero s se genera cuyos dígitos se utilizarán en la representación de Cadena de metro:

dejar norte, ky s ser enteros tales que k ≥ 1, 10k-1 ≤ s <10k, el valor de Número para s × 10norte-k es metroy k es lo más pequeño posible. Tenga en cuenta que k es el número de dígitos en la representación decimal de s, ese s no es divisible por 10, y que el dígito menos significativo de s no está necesariamente determinado de manera única por estos criterios.

La parte clave aquí es el requisito de que "k es lo más pequeño posible ". Lo que ese requisito equivale a un requisito que, dado un número m, El valor de String(m) debe tener la menor cantidad posible de dígitos sin dejar de cumplir el requisito de que Number(String(m)) === m. Como ya sabemos eso 0.015 === 0.0150000000000000001, ahora está claro por qué String(0.0150000000000000001) === '0.015' debe ser verdad

Por supuesto, nada de esta discusión ha respondido directamente roundTo2DP(m)  debería regreso. Si mEl valor exacto es 0.01499999999999999944488848768742172978818416595458984375, pero su representación de cadena es '0.015', entonces ¿cuál es la correcto respuesta matemática, práctica, filosófica o lo que sea, cuando lo redondeamos a dos decimales?

No hay una única respuesta correcta para esto. Depende de tu caso de uso. Probablemente desee respetar la representación de Cadena y redondear hacia arriba cuando:

  • El valor que se representa es intrínsecamente discreto, p. una cantidad de moneda en una moneda de 3 decimales como dinares. En este caso, el cierto valor de un número como 0.015 es 0.015, y la representación 0.0149999999 ... que obtiene en punto flotante binario es un error de redondeo. (Por supuesto, muchos argumentarán, razonablemente, que debe usar una biblioteca decimal para manejar dichos valores y nunca representarlos como Números binarios de coma flotante en primer lugar).
  • El valor fue escrito por un usuario. En este caso, nuevamente, el número decimal exacto ingresado es más 'verdadero' que la representación de punto flotante binario más cercano.

Por otro lado, es probable que desee respetar el valor del punto flotante binario y redondear hacia abajo cuando su valor proviene de una escala inherentemente continua, por ejemplo, si es una lectura de un sensor.

Estos dos enfoques requieren un código diferente. Para respetar la representación de Cadena del Número, podemos (con un poco de código razonablemente sutil) implementar nuestro propio redondeo que actúa directamente en la representación de Cadena, dígito por dígito, utilizando el mismo algoritmo que hubieras utilizado en la escuela cuando se les enseñó a redondear números. A continuación se muestra un ejemplo que respeta el requisito del OP de representar el número a 2 decimales "solo cuando sea necesario" eliminando los ceros finales después del punto decimal; puede, por supuesto, necesitar ajustarlo a sus necesidades precisas.

/**
 * Converts num to a decimal string (if it isn't one already) and then rounds it
 * to at most dp decimal places.
 *
 * For explanation of why you'd want to perform rounding operations on a String
 * rather than a Number, see http://stackoverflow.com/a/38676273/1709587
 *
 * @param {(number|string)} num
 * @param {number} dp
 * @return {string}
 */
function roundStringNumberWithoutTrailingZeroes (num, dp) {
    if (arguments.length != 2) throw new Error("2 arguments required");

    num = String(num);
    if (num.indexOf('e+') != -1) {
        // Can't round numbers this large because their string representation
        // contains an exponent, like 9.99e+37
        throw new Error("num too large");
    }
    if (num.indexOf('.') == -1) {
        // Nothing to do
        return num;
    }

    var parts = num.split('.'),
        beforePoint = parts[0],
        afterPoint = parts[1],
        shouldRoundUp = afterPoint[dp] >= 5,
        finalNumber;

    afterPoint = afterPoint.slice(0, dp);
    if (!shouldRoundUp) {
        finalNumber = beforePoint + '.' + afterPoint;
    } else if (/^9+$/.test(afterPoint)) {
        // If we need to round up a number like 1.9999, increment the integer
        // before the decimal point and discard the fractional part.
        finalNumber = Number(beforePoint)+1;
    } else {
        // Starting from the last digit, increment digits until we find one
        // that is not 9, then stop
        var i = dp-1;
        while (true) {
            if (afterPoint[i] == '9') {
                afterPoint = afterPoint.substr(0, i) +
                             '0' +
                             afterPoint.substr(i+1);
                i--;
            } else {
                afterPoint = afterPoint.substr(0, i) +
                             (Number(afterPoint[i]) + 1) +
                             afterPoint.substr(i+1);
                break;
            }
        }

        finalNumber = beforePoint + '.' + afterPoint;
    }

    // Remove trailing zeroes from fractional part before returning
    return finalNumber.replace(/0+$/, '')
}

Ejemplo de uso:

> roundStringNumberWithoutTrailingZeroes (1.6, 2)
'1.6'
> roundStringNumberWithoutTrailingZeroes (10000, 2)
'10000'
> roundStringNumberWithoutTrailingZeroes (0.015, 2)
'0.02'
> roundStringNumberWithoutTrailingZeroes ('0.015000', 2)
'0.02'
> roundStringNumberWithoutTrailingZeroes (1, 1)
'1'
> roundStringNumberWithoutTrailingZeroes ('0.015', 2)
'0.02'
> roundStringNumberWithoutTrailingZeroes (0.01499999999999999944488848768742172978818416595458984375, 2)
'0.02'
> roundStringNumberWithoutTrailingZeroes ('0.01499999999999999944488848768742172978818416595458984375', 2)
'0.01'

La función anterior es probablemente lo que quiere usar para evitar que los usuarios presencien que los números ingresados ​​se redondean incorrectamente.

(Como alternativa, también puedes probar el round10 biblioteca que proporciona una función de comportamiento similar con una implementación muy diferente).

Pero, ¿qué sucede si tiene el segundo tipo de Número, un valor tomado de una escala continua, donde no hay ninguna razón para pensar que las representaciones decimales aproximadas con menos decimales son más preciso que aquellos con más? En ese caso, nosotros no lo hagas quiere respetar la representación de Cadena, porque esa representación (como se explica en la especificación) ya está ordenada; no queremos cometer el error de decir "0.014999999 ... 375 redondea hasta 0.015, que redondea hasta 0.02, entonces 0.014999999 ... 375 redondea hasta 0.02".

Aquí podemos simplemente usar el built-in toFixed método. Tenga en cuenta que al llamar Number() en la cadena devuelta por toFixed, obtenemos un número cuya representación de cadena no tiene ceros finales (gracias a la forma en que JavaScript calcula la representación en cadena de un número, discutido anteriormente en esta respuesta).

/**
 * Takes a float and rounds it to at most dp decimal places. For example
 *
 *     roundFloatNumberWithoutTrailingZeroes(1.2345, 3)
 *
 * returns 1.234
 *
 * Note that since this treats the value passed to it as a floating point
 * number, it will have counterintuitive results in some cases. For instance,
 * 
 *     roundFloatNumberWithoutTrailingZeroes(0.015, 2)
 *
 * gives 0.01 where 0.02 might be expected. For an explanation of why, see
 * http://stackoverflow.com/a/38676273/1709587. You may want to consider using the
 * roundStringNumberWithoutTrailingZeroes function there instead.
 *
 * @param {number} num
 * @param {number} dp
 * @return {number}
 */
function roundFloatNumberWithoutTrailingZeroes (num, dp) {
    var numToFixedDp = Number(num).toFixed(dp);
    return Number(numToFixedDp);
}

53
2017-07-30 16:47



Considerar .toFixed() y .toPrecision():

http://www.javascriptkit.com/javatutors/formatnumber.shtml


51
2017-08-06 17:21