¿Eliminar ceros finales insignificantes de un número?


157

¿Me he perdido una llamada API estándar que elimina ceros insignificantes finales de un número?

Ex.

var x = 1.234000 // to become 1.234;
var y = 1.234001; // stays 1.234001

Number.toFixed () y Number.toPrecision () no son exactamente lo que estoy buscando.


66
Um, 1.234000 === 1.234.
Gumbo

55
Sí, si alerta (x) aparece sin los ceros finales
JKirchartz

50
Después de trabajar con varios clientes, puedo testificar que aunque 1.234000 === 1.234, los clientes no quieren ver esos ceros adicionales si no lo necesitan.
contactmatt

16
Uso parseFloat(n)?
Sr. Alien

Esta fue la solución más fácil que cubrió todos los casos extremos para mí, gracias @ Mr.Alien.
James Perih

Respuestas:


140

Si lo convierte en una cadena, no mostrará ceros finales, que no se almacenan en la variable en primer lugar, ya que se creó como un Número, no como una Cadena.

var n = 1.245000
var noZeroes = n.toString() // "1.245" 

66
Estaba a punto de publicar un código para eliminar los ceros, pero la solución de Daniel parece funcionar. Incluso funciona para cadenas como "1.2345000". ("1.2345000" * 1) .toString (); // se convierte en 1.2345
Steven

77
Por supuesto, si su var ya es una cadena, primero tiene que convertirla en un número y luego volverla a
activar

Esto funcionó muy bien. Estaba teniendo el mismo problema, convertí flotante a cadena y luego analizó flotante para recuperarlo en el formato correcto, solo sin los ceros finales.
Matt West el

10
Esta no es la solución completa. No funciona cuando tiene una variable con algún error de representación de coma flotante, como: var n = 1.245000000000001(suponiendo que sea insignificante ser representado por el usuario)
agosto

77
Puede producir resultados inesperados para números como: 1231111111111111111111111111111222222222222222222222222222222222222222222222222222.00. La representación de la cadena estará en formato exponencial: 1.231111111111111e + 81
Stas Makutin

227

Tuve una instancia similar en la que quería usarla .toFixed()cuando fuera necesario, pero no quería el relleno cuando no era así. Así que terminé usando parseFloat junto con toFixed.

Fijado sin relleno

parseFloat(n.toFixed(4));

Otra opción que hace casi lo mismo.
Esta respuesta puede ayudar a su decisión.

Number(n.toFixed(4));

toFixedredondeará / rellenará el número a una longitud específica, pero también lo convertirá en una cadena. Convertirlo de nuevo a un tipo numérico no solo hará que el número sea más seguro de usar aritméticamente, sino que también eliminará automáticamente los ceros finales. Por ejemplo:

var n = "1.234000";
    n = parseFloat(n);
 // n is 1.234 and in number form

Porque incluso si define un número con ceros finales, se eliminan.

var n = 1.23000;
 // n == 1.23;

Tenga en cuenta que esto solo funciona si el número relevante de decimales es igual o mayor que el argumento toFixed. Si el número es, por ejemplo, 1.200000, el resultado de toFixed (3) será 1.200 y, por lo tanto, no se eliminarán todos los ceros finales.
Leopoldo Sanczyk

@LeopoldoSanczyk No, eso solo es cierto si solo está usando toFixed, porque devuelve una cadena. Los tipos de números pierden automáticamente ceros finales. Es por eso que usé los dos en tándem.
Gary

@ Gary, retiro mi comentario, he visto en parseFloat(n).toFixed(4);lugar de tu respuesta real. Lo siento.
Leopoldo Sanczyk

2
¿Por qué no +(n.toFixed(4))?
Константин Ван

3
¡Votado! solo un problema con su respuesta 0.00000005 se convierte a 5e-8, ¿cómo puedo eliminar ceros insignificantes sin este problema? Estoy trabajando con números pequeños como 0.000058000 donde los ceros al final deben eliminarse
PirateApp

29

Primero usé una combinación de las respuestas de matti-lyra y gary:

r=(+n).toFixed(4).replace(/\.0+$/,'')

Resultados:

  • 1234870.98762341: "1234870.9876"
  • 1230009100: "1230009100"
  • 0.0012234: "0.0012"
  • 0.1200234: "0.12"
  • 0.000001231: "0"
  • 0.10001: "0.1000"
  • "asdf": "NaN" (por lo que no hay error de tiempo de ejecución)

El caso un tanto problemático es 0.10001. Terminé usando esta versión más larga:

    r = (+n).toFixed(4);
    if (r.match(/\./)) {
      r = r.replace(/\.?0+$/, '');
    }
  • 1234870.98762341: "1234870.9876"
  • 1230009100: "1230009100"
  • 0.0012234: "0.0012"
  • 0.1200234: "0.12"
  • 0.000001231: "0"
  • 0.10001: "0.1"
  • "asdf": "NaN" (por lo que no hay error de tiempo de ejecución)

Actualización : Y esta es la versión más nueva de Gary (ver comentarios):

r=(+n).toFixed(4).replace(/([0-9]+(\.[0-9]+[1-9])?)(\.?0+$)/,'$1')

Esto da los mismos resultados que arriba.


1
Tengo una solución regex pura que creo que funcionatoFixed(4).replace(/([0-9]+(\.[1-9]+)?)(\.?0+$)/,"$1")
Gary

1
Gah! Aparentemente no soy tan bueno rompiendo mis propios RegExes como otros. ¡No dejaré que esto me gane! Corrí este contra todos sus casos de prueba más cualquier (válido) que se me ocurriera([0-9]+(\.[0-9]+[1-9])?)(\.?0+$)
Gary

1
¡La expresión regular es codiciosa y a veces elimina un "0" de la parte entera! :-( ¿Por qué no solo replace(/[,.][1-9]+(0+)/,'')?
Sebastian Sebald

1
Subestimado responda esto.
Charlie

1
@ w00t Oh, lo siento, ahora entiendo. Era la forma en que probé cuando hice la mía (ver enlace arriba). El suyo no falla cuando usa toFixed, porque el número está garantizado que tiene un punto, pero fallaba en mis pruebas porque quería que la expresión regular funcionara para cualquier número, incluidos los enteros sin punto. Sin toFixed, se come un cero al final. Culpa mía.
geekley

22

El toFixedmétodo hará el redondeo apropiado si es necesario. También agregará ceros finales, lo que no siempre es ideal.

(4.55555).toFixed(2);
//-> "4.56"

(4).toFixed(2);
//-> "4.00"

Si convierte el valor de retorno en un número, esos ceros finales se eliminarán. Este es un enfoque más simple que hacer sus propios cálculos de redondeo o truncamiento.

+(4.55555).toFixed(2);
//-> 4.56

+(4).toFixed(2);
//-> 4

1
complicado, pero exactamente lo que he estado buscando: eliminar los ceros finales significativos (que por supuesto hicimos significativos a través de la toFixedllamada). Es un caso extraño UX edge.
worc

12

Tenía básicamente el mismo requisito, y descubrí que no hay un mecanismo incorporado para esta funcionalidad.

Además de recortar los ceros finales, también tuve la necesidad de redondear y formatear la salida para la configuración regional actual del usuario (es decir, 123,456.789).

Todo mi trabajo en esto se ha incluido como prettyFloat.js (con licencia MIT) en GitHub: https://github.com/dperish/prettyFloat.js


Ejemplos de uso:

prettyFloat(1.111001, 3) // "1.111"
prettyFloat(1.111001, 4) // "1.111"
prettyFloat(1.1111001, 5) // "1.1111"
prettyFloat(1234.5678, 2) // "1234.57"
prettyFloat(1234.5678, 2, true) // "1,234.57" (en-us)


Actualizado - agosto de 2018


Todos los navegadores modernos ahora son compatibles con la API de internacionalización ECMAScript , que proporciona una comparación de cadenas sensible al idioma, formato de número y formato de fecha y hora.

let formatters = {
    default: new Intl.NumberFormat(),
    currency: new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD', minimumFractionDigits: 0, maximumFractionDigits: 0 }),
    whole: new Intl.NumberFormat('en-US', { style: 'decimal', minimumFractionDigits: 0, maximumFractionDigits: 0 }),
    oneDecimal: new Intl.NumberFormat('en-US', { style: 'decimal', minimumFractionDigits: 1, maximumFractionDigits: 1 }),
    twoDecimal: new Intl.NumberFormat('en-US', { style: 'decimal', minimumFractionDigits: 2, maximumFractionDigits: 2 })
};

formatters.twoDecimal.format(1234.5678);  // result: "1,234.57"
formatters.currency.format(28761232.291); // result: "$28,761,232"

Para navegadores antiguos, puede usar este polyfill: https://cdn.polyfill.io/v2/polyfill.min.js?features=Intl.~locale.en


Esto es lo que estabas buscando. Debe crear un paquete npm.
EnZo

Por lo tanto, probablemente debería actualizar esto, ya que ahora esto es posible a través de la API de internacionalización.
Dperish

2
Esto es muy útil, gracias por compartir y gracias por la actualización también.
jaggedsoft

11

¿Qué tal simplemente multiplicar por uno como este?

var x = 1.234000*1; // becomes 1.234

var y = 1.234001*1; // stays as 1.234001

9

Puedes probar este para minimizar números flotantes

var n = 0.0000;
n = parseFloat(n.toString()); 

//output n = 0; 
// n = 3.14000; --> n = 3.14;

7

Respuesta pura regex

n.replace(/(\.[0-9]*[1-9])0+$|\.0*$/,'$1');

¡Me pregunto por qué nadie dio uno!


No funcionará con números (los números se indican en la pregunta original). ¡Sin embargo, me encanta la solución para una representación de cadena!
BennyHilarante el

6

También necesitaba resolver este problema cuando Django mostraba valores de tipo Decimal en un campo de texto. Por ejemplo, cuando '1' era el valor. Mostraría '1.00000000'. Si '1.23' fuera el valor, mostraría '1.23000000' (en el caso de una configuración 'decimal_places' de 8)

Usar parseFloat no era una opción para mí, ya que es posible que no devuelva exactamente el mismo valor. toFixed no era una opción ya que no quería redondear nada, así que creé una función:

function removeTrailingZeros(value) {
    value = value.toString();

    # if not containing a dot, we do not need to do anything
    if (value.indexOf('.') === -1) {
        return value;
    }

    # as long as the last character is a 0 or a dot, remove it
    while((value.slice(-1) === '0' || value.slice(-1) === '.') && value.indexOf('.') !== -1) {
        value = value.substr(0, value.length - 1);
    }
    return value;
}

Esto funciona, sin embargo, es bastante ineficiente, ya que resulta en la creación de múltiples cadenas por ejecución. Imagine ejecutar esto en todas las celdas de una tabla, donde un buen número fue .00000. Una mejor opción sería determinar cuántos ceros a la izquierda hay, luego hacer el corte de una vez. Determinar qué es un personaje es, en gran medida, eficiente, dividir cadenas no lo es tanto.
Derek Nigel Bartram

Hice algo similar a tu sugerencia Derek: github.com/rek/remove-trailing-zeros/blob/master/…
rekarnar

La prueba de rendimiento está aquí: jsperf.com/remove-trailing-zeros/1 , tenías razón, ¡encontrar los ceros y eliminarlos es mucho más rápido!
rekarnar

4

Ninguna de estas soluciones me funcionó para números muy pequeños. http://numeraljs.com/ resolvió esto por mí.

parseFloat(0.00000001.toFixed(8));
// 1e-8

numeral(0.00000001).format('0[.][00000000]');
// "0.00000001"

No puede manejar más de 6 precisión. Yo diría que esta biblioteca es inútil si está destinada a números pequeños. Definitivamente no recomendado.
Daniel Kmak

2

Si no puede usar Flotadores por algún motivo (como los flotadores de dinero involucrados) y ya está comenzando desde una cadena que representa un número correcto, puede encontrar esta solución a mano. Convierte una cadena que representa un número en una cadena que representa un número sin ceros finales.

function removeTrailingZeroes( strAmount ) {
    // remove all trailing zeroes in the decimal part
    var strDecSepCd = '.'; // decimal separator
    var iDSPosition = strAmount.indexOf( strDecSepCd ); // decimal separator positions
    if ( iDSPosition !== -1 ) {
        var strDecPart = strAmount.substr( iDSPosition ); // including the decimal separator

        var i = strDecPart.length - 1;
        for ( ; i >= 0 ; i-- ) {
            if ( strDecPart.charAt(i) !== '0') {
                break;
            }
        }

        if ( i=== 0 ) {
            return strAmount.substring(0, iDSPosition);
        } else {
            // return INTPART and DS + DECPART including the rightmost significant number
            return strAmount.substring(0, iDSPosition) + strDecPart.substring(0,i + 1);
        }
    }

    return strAmount;
}

0

Después de leer todas las respuestas, y comentarios, terminé con esto:

function isFloat(n) {
    let number = (Number(n) === n && n % 1 !== 0) ? eval(parseFloat(n)) : n;
    return number;
}

Sé usar eval puede ser dañino de alguna manera, pero esto me ayudó mucho.

Entonces:

isFloat(1.234000);     // = 1.234;
isFloat(1.234001);     // = 1.234001
isFloat(1.2340010000); // = 1.234001

Si desea limitar los lugares decimales, úselos toFixed()como otros señalaron.

let number = (Number(n) === n && n % 1 !== 0) ? eval(parseFloat(n).toFixed(3)) : n;

Eso es.


0

Necesitaba eliminar los ceros finales pero mantener al menos 2 decimales, incluidos los ceros.
Los números con los que estoy trabajando son 6 cadenas de números decimales, generadas por .toFixed (6).

Resultado Esperado:

var numstra = 12345.000010 // should return 12345.00001
var numstrb = 12345.100000 // should return 12345.10
var numstrc = 12345.000000 // should return 12345.00
var numstrd = 12345.123000 // should return 12345.123

Solución:

var numstr = 12345.100000

while (numstr[numstr.length-1] === "0") {           
    numstr = numstr.slice(0, -1)
    if (numstr[numstr.length-1] !== "0") {break;}
    if (numstr[numstr.length-3] === ".") {break;}
}

console.log(numstr) // 12345.10

Lógica:

Ejecute la función de bucle si el último carácter de cadena es un cero.
Elimine el último carácter y actualice la variable de cadena.
Si el último carácter de cadena actualizado no es un cero, finalice el bucle.
Si la cadena actualizada del tercer al último carácter es un punto flotante, finalice el bucle.


-1

Aquí hay una posible solución:

var x = 1.234000 // to become 1.234;
var y = 1.234001; // stays 1.234001

eval(x) --> 1.234
eval(y) --> 1.234001

44
¡No necesitas evaluar para esta simple tarea! parseFloat()Será muy apropiado.
hutchbat

66
eval == maldad. No lo uses nunca
eagor
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.