Javascript: formatear un número redondeado a N decimales

104

en JavaScript, la forma típica de redondear un número a N posiciones decimales es algo como:

function roundNumber(num, dec) {
  return Math.round(num * Math.pow(10, dec)) / Math.pow(10, dec);
}

Sin embargo, este enfoque se redondeará a un máximo de N lugares decimales, mientras que yo siempre quiero redondear a N lugares decimales. Por ejemplo, "2.0" se redondearía a "2".

¿Algunas ideas?

hoju
fuente
9
normalmente, podría usar toFixed()( developer.mozilla.org/En/Core_JavaScript_1.5_Reference/… ), pero tiene errores en IE: stackoverflow.com/questions/661562/… ; tendrás que escribir tu propia versión ...
Christoph
1
@hoju - quizás cambiar la respuesta aceptada - La respuesta de David es correcta para IE8 +, mientras que la respuesta aceptada tiene algunos errores graves en todos los navegadores.
robocat
@robocat: ¿Hablas en serio?
Guffa

Respuestas:

25

Eso no es un problema de redondeo, es un problema de visualización. Un número no contiene información sobre dígitos significativos; el valor 2 es igual a 2.0000000000000. Es cuando convierte el valor redondeado en una cadena que hace que muestre una cierta cantidad de dígitos.

Puede agregar ceros después del número, algo como:

var s = number.toString();
if (s.indexOf('.') == -1) s += '.';
while (s.length < s.indexOf('.') + 4) s += '0';

(Tenga en cuenta que esto supone que la configuración regional del cliente usa un punto como separador decimal, el código necesita más trabajo para funcionar para otras configuraciones).

Guffa
fuente
3
toFixed tiene errores ... por ejemplo, el decimal: 1.02449999998 si dec.toFixed (4) => 1.0244. Debería haber sido 1.0245 en su lugar.
Bat_Programmer
2
@deepeshk Pero, ¿cuál sería el problema de usar toFixed()para rellenar decimales al final, después de redondear?
pauloya
10
@deepeshk ¿en qué navegador? Solo lo probé en Chrome 17 y 1.02449999998.toFixed(4)regresa correctamente 1.0245.
Matt Ball
3
@MarkTomlin: Entonces parece que tienes una cadena en lugar de un número.
Guffa
1
.toFixed () funciona de manera brillante en los navegadores modernos (probé Chrome, Firefox, IE11, IE8). @Bat_Programmer: aclara qué navegadores crees que tienen ese error.
robocat
243

Creo que hay un enfoque más simple para todos aquí, y es el método Number.toFixed()ya implementado en JavaScript.

simplemente escribe:

var myNumber = 2;

myNumber.toFixed(2); //returns "2.00"
myNumber.toFixed(1); //returns "2.0"

etc ...

David
fuente
¿Dónde se menciona, hoju? Revisé otras respuestas y no encontré a nadie que informara que la función toFixed tiene errores. Gracias
David
@David: Eso se menciona en un comentario a la respuesta, por ejemplo.
Guffa
13
precaución: toFixed()devuelve una cadena.
Jordan Arseno
2
@JordanArseno, esto se puede arreglar usando parseInt (myNumber.toFixed (intVar)); para devolver un valor entero, o parseFloat (myNumber.toFixed (floatVar)); para devolver un flotante si el usuario tiene decimales.
Stephen Romero
50

Encontre un camino. Este es el código de Christoph con una solución:

function toFixed(value, precision) {
    var precision = precision || 0,
        power = Math.pow(10, precision),
        absValue = Math.abs(Math.round(value * power)),
        result = (value < 0 ? '-' : '') + String(Math.floor(absValue / power));

    if (precision > 0) {
        var fraction = String(absValue % power),
            padding = new Array(Math.max(precision - fraction.length, 0) + 1).join('0');
        result += '.' + padding + fraction;
    }
    return result;
}

Lea los detalles de la repetición de un carácter usando un constructor de matriz aquí si tiene curiosidad por saber por qué agregué el "+ 1".

Elias Zamaria
fuente
Pasar un valor de 708,3333333333333 con una precisión de 2 o 3 da como resultado un valor de retorno de 708,00 para mí. Necesito esto para 2 lugares decimales, en Chrome e IE 9 .toFixed (2) satisfizo mis necesidades.
Alan
esta no es una forma común, por ejemplo, toFixed (16.775, 2) return 16.77. Convertir número en cadena y luego convertir es la única forma.
hiway
2
Hay un error con este método: toFixed (-0.1111, 2) devuelve 0.11, es decir, se pierde el signo negativo.
Matt
Funciona muy bien, excepto que agregué parseFloat (resultado) al final. Vale la pena editarlo.
Artur Barseghyan
16

Siempre hay una mejor manera de hacer las cosas.

var number = 51.93999999999761;

Me gustaría obtener una precisión de cuatro dígitos: 51,94

solo haz:

number.toPrecision(4);

el resultado será: 51,94

de.la.ru
fuente
2
¿Y si suma 100? ¿Necesita cambiarlo a number.toPrecision (5)?
JohnnyBizzle
2
Si. 31.939383.toPrecision (4)> "31.94" / 131.939383.toPrecision (4)> "131.9"
ReSpawN
6

Método de redondeo similar a PHP

El siguiente código se puede usar para agregar su propia versión de Math.round a su propio espacio de nombres que toma un parámetro de precisión. A diferencia del redondeo decimal en el ejemplo anterior, este no realiza conversión hacia y desde cadenas, y el parámetro de precisión funciona de la misma manera que PHP y Excel, por lo que un 1 positivo se redondearía a 1 lugar decimal y -1 se redondearía a las decenas.

var myNamespace = {};
myNamespace.round = function(number, precision) {
    var factor = Math.pow(10, precision);
    var tempNumber = number * factor;
    var roundedTempNumber = Math.round(tempNumber);
    return roundedTempNumber / factor;
};

myNamespace.round(1234.5678, 1); // 1234.6
myNamespace.round(1234.5678, -1); // 1230

de la referencia de desarrollador de Mozilla para Math.round ()

JohnnyBizzle
fuente
2

Con suerte, código de trabajo (no hice muchas pruebas):

function toFixed(value, precision) {
    var precision = precision || 0,
        neg = value < 0,
        power = Math.pow(10, precision),
        value = Math.round(value * power),
        integral = String((neg ? Math.ceil : Math.floor)(value / power)),
        fraction = String((neg ? -value : value) % power),
        padding = new Array(Math.max(precision - fraction.length, 0) + 1).join('0');

    return precision ? integral + '.' +  padding + fraction : integral;
}
Christoph
fuente
Tu código tiene un error. Traté de arreglar (2.01, 4) y obtuve un resultado de "2.100". Si alguna vez encuentro una manera de solucionarlo, lo publicaré como respuesta a esta pregunta.
Elias Zamaria
@ mikez302: el cálculo de relleno estaba desviado en uno; debería funcionar ahora, pero siéntase libre de molestarme de nuevo si todavía está roto ...
Christoph
Muy extraño. Cuando ejecuto esta función con la consola Firebug abierta en Firefox 17, congela todo el navegador como si js estuviera atrapado en un bucle sin fin. Incluso si no consuelo.log la salida. Si no tengo activado el firebug, el error no ocurre.
SublymeRick
1
Actualización, lo ejecuté en Chrome y obtengo: Uncaught RangeError: Se excedió el tamaño máximo de la pila de llamadas con respecto a la llamada a la función toFixed.
SublymeRick
@SublymeRick: No tengo idea de por qué sucede esto; disparo en la oscuridad: intente cambiar el nombre de la función ...
Christoph
2

Creo que la siguiente función puede ayudar

function roundOff(value,round) {
   return (parseInt(value * (10 ** (round + 1))) - parseInt(value * (10 ** round)) * 10) > 4 ? (((parseFloat(parseInt((value + parseFloat(1 / (10 ** round))) * (10 ** round))))) / (10 ** round)) : (parseFloat(parseInt(value * (10 ** round))) / ( 10 ** round));
}

uso: roundOff(600.23458,2);volverá600.23

KRISHNA TEJA
fuente
2

Esto funciona para redondear a N dígitos (si solo desea truncar a N dígitos, elimine la llamada Math.round y use la Math.trunc):

function roundN(value, digits) {
   var tenToN = 10 ** digits;
   return /*Math.trunc*/(Math.round(value * tenToN)) / tenToN;
}

Tuve que recurrir a esa lógica en Java en el pasado cuando estaba creando componentes de E-Slate de manipulación de datos . Eso es porque descubrí que agregando 0.1 muchas veces a 0 terminaría con una parte decimal inesperadamente larga (esto se debe a la aritmética de punto flotante).

Un comentario de usuario en Número de formato para mostrar siempre 2 lugares decimales llama a esta técnica escalado.

Algunos mencionan que hay casos que no se redondean como se esperaba y en http://www.jacklmoore.com/notes/rounding-in-javascript/ esto se sugiere en su lugar:

function round(value, decimals) {
  return Number(Math.round(value+'e'+decimals)+'e-'+decimals);
}
George Birbilis
fuente
Por cierto, POSITS es prometedor para futuras arquitecturas h / w: nextplatform.com/2019/07/08/…
George Birbilis
-2

Si realmente no le importa el redondeo, simplemente agregue un toFixed (x) y luego elimine los ceros finales y el punto si es necesario. No es una solución rápida.

function format(value, decimals) {
    if (value) {
        value = value.toFixed(decimals);            
    } else {
        value = "0";
    }
    if (value.indexOf(".") < 0) { value += "."; }
    var dotIdx = value.indexOf(".");
    while (value.length - dotIdx <= decimals) { value += "0"; } // add 0's

    return value;
}
Juan Carrey
fuente
Lo intenté format(1.2, 5)y lo conseguí 1.2, mientras esperaba 1.20000.
Elias Zamaria
Lo siento @EliasZamaria, no lo entendí al principio. He editado la publicación. Solo tenga en cuenta que devolverá "0.0000" cuando el valor no esté definido.
Juan Carrey