¿Cómo puedo redondear un número en JavaScript? .toFixed () devuelve una cadena?

176

¿Me estoy perdiendo de algo?

var someNumber = 123.456;
someNumber = someNumber.toFixed(2);
alert(typeof(someNumber));
//alerts string

¿ Por qué.toFixed() devuelve una cadena?

Quiero redondear el número a 2 dígitos decimales.

Derek Adair
fuente
77
¿Porque está diseñado para devolver una cadena?
kennytm
2
A mí me parece extraño. .toFixed () solo funciona con números ... ¿verdad?
Derek Adair
10
Entiendo que Math.round () funciona como se esperaba. Estaba preguntando por qué una función que funciona con números devuelve una cadena ...
Derek Adair
3
Las personas que viven en 2017 deberían usar bibliotecas como lodash.com/docs/4.17.4#ceil
Yves M.
1
También lo hace _. ¿contar? no actualizado a su hermano todavía.
Jenna Leaf

Respuestas:

124

Devuelve una cadena porque 0.1, y sus potencias (que se usan para mostrar fracciones decimales), no son representables (al menos no con total precisión) en sistemas binarios de punto flotante.

Por ejemplo, 0.1 es realmente 0.1000000000000000055511151231257827021181583404541015625, y 0.01 es realmente 0.01000000000000000020816681711721685132943093776702880859375. (Gracias a BigDecimalpor demostrar mi punto. :-P)

Por lo tanto (en ausencia de coma flotante decimal o tipo de número racional), su salida como una cadena es la única forma de recortarlo exactamente con la precisión requerida para la visualización.

Chris Jester-Young
fuente
28
al menos javascript podría ahorrarme algo de trabajo con los dedos y convertirlo de nuevo a un número ... sheesh ...
Derek Adair
10
@Derek: Sí, pero una vez que lo conviertes nuevamente en un número, te encuentras con los mismos problemas de inexactitud nuevamente. :-P JS no tiene coma flotante decimal o números racionales.
Chris Jester-Young
1
@DerekAdair Recientemente escribí una publicación que explica esto aún más, que te puede interesar. ¡Disfruta! stackoverflow.com/a/27030789/13
Chris Jester-Young
77
En realidad, esto me llevó a hacer una investigación bastante pesada sobre este tema. ¡Gracias por toda tu ayuda!
Derek Adair
2
Su respuesta es ligeramente engañosa: toFixedes una función de formateo, que tiene el único propósito de convertir un número en una cadena, formateándola usando el número especificado de decimales. La razón por la que devuelve una cadena es porque se supone que devuelve una cadena, y si se nombrara toStringFixeden su lugar, OP no se sorprendería de los resultados. El único problema aquí es que OP esperaba que funcionara Math.roundsin consultar la referencia de JS.
Groo
177

Number.prototype.toFixedes una función diseñada para formatear un número antes de imprimirlo. Es de la familia de toString, toExponentialy toPrecision.

Para redondear un número, haría esto:

someNumber = 42.008;
someNumber = Math.round( someNumber * 1e2 ) / 1e2;
someNumber === 42.01;

// if you need 3 digits, replace 1e2 with 1e3 etc.
// or just copypaste this function to your code:

function toFixedNumber(num, digits, base){
  var pow = Math.pow(base||10, digits);
  return Math.round(num*pow) / pow;
}

.

O si desea una función " nativa ", puede extender el prototipo:

Number.prototype.toFixedNumber = function(digits, base){
  var pow = Math.pow(base||10, digits);
  return Math.round(this*pow) / pow;
}
someNumber = 42.008;
someNumber = someNumber.toFixedNumber(2);
someNumber === 42.01;


//or even hexadecimal

someNumber = 0xAF309/256  //which is af3.09
someNumber = someNumber.toFixedNumber(1, 16);
someNumber.toString(16) === "af3.1";

Sin embargo , tenga en cuenta que contaminar el prototipo se considera malo cuando está escribiendo un módulo, ya que los módulos no deberían tener ningún efecto secundario. Entonces, para un módulo, use la primera función .

m93a
fuente
12
Creo que esta es la mejor respuesta. Evita la conversión de tipos. ¡Salsa impresionante!
Phil
1
¡Gran respuesta! Sin embargo ... He estado haciendo JavaScript como 20 años más o menos, pero no puedo entender por qué estás usando esa construcción + (...) alrededor del valor de retorno. Gracias @sam por frotarlo :) Como nunca soy demasiado viejo para aprender, por favor explique :-)
HammerNL
1
@HammerNL A pesar de la convicción de Sam, no hace nada de manera aguda :) Es solo una práctica: hace que los IDE reconozcan esta función type Number. La cosa es que +(anyValue)siempre devuelve un número, por ejemplo. +("45")vuelve 45, +(new Number(42))vuelve 42. Es como escribir una función fuerte. Si tiene el hábito, puede evitar muchos errores :)
m93a
¿Por qué esto no está integrado en javascript central? S
webmaster
2
El resultado de someNumber = Math.round( 42.008 * 1e2 ) / 1e2;no es 42.01, es ~42.0099999999999980. Motivo: El número 42.01no existe y se redondea al número existente más cercano. por cierto, prueba los números toPrecision(18)para imprimirlo con todos los dígitos relevantes.
Wiimm
118

He resuelto este problema cambiando esto:

someNumber = someNumber.toFixed(2)

...a esto:

someNumber = +someNumber.toFixed(2);

Sin embargo, esto convertirá el número en una cadena y lo analizará nuevamente, lo que tendrá un impacto significativo en el rendimiento. Si le preocupa el rendimiento o la seguridad de los tipos, verifique también las otras respuestas.

Eva juan
fuente
39
¡No no no no no! ¡No hagas eso! ¡La conversión de número a cadena solo para redondear es una muy mala práctica ! En cambio hazlo someNumber = Math.round(someNumber * 1e2) / 1e2! Vea mi respuesta para una forma más generalizada.
m93a
@ m93a: ¿por qué es tan mala práctica?
jczaplew
3
@jczaplew Porque si lo haces de esta manera, el número binario de 32 bits se convierte en una cadena, ¡usando 16 bits por cada maldito dígito decimal ! (Por cierto, almacenar números en UTF-16 no es lo más conveniente). Y la cadena se convierte de nuevo en un número de coma flotante de 32 bits. Dígito por dígito. (Si ignoro todas las pruebas que deben hacerse antes de elegir el algoritmo de análisis correcto). Y todo eso en vano teniendo en cuenta que puede hacerlo utilizando 3 operaciones rápidas en el flotador.
m93a
2
@ m93a, ¿por razones de rendimiento? ¿esto realmente tiene un impacto notable en el rendimiento?
Sebastianb
2
@jczaplew, porque las cadenas son (1) prácticamente lentas y (2) teóricamente incorrectas.
Pacerier
28

¿Por qué no usar parseFloat?

var someNumber = 123.456;
someNumber = parseFloat(someNumber.toFixed(2));
alert(typeof(someNumber));
//alerts number
sirlunchalot
fuente
15

Lo resolví convirtiéndolo de nuevo en número usando la Number()función de JavaScript

var x = 2.2873424;
x = Number(x.toFixed(2));
Nizar
fuente
12

Por supuesto que devuelve una cadena. Si quisiera redondear la variable numérica, usaría Math.round () en su lugar. El objetivo de toFixed es formatear el número con un número fijo de decimales para mostrar al usuario .

Joel Coehoorn
fuente
4

Simplemente puede usar un '+' para convertir el resultado en un número.

var x = 22.032423;
x = +x.toFixed(2); // x = 22.03
meisam
fuente
3

¿Qué esperarías que devuelva cuando se supone que formatea un número? Si tiene un número, no puede hacer nada con él, por ejemplo, 2 == 2.0 == 2.00etc., por lo que tiene que ser una cadena.

Tomás Vana
fuente
3

Para proporcionar un ejemplo de por qué tiene que ser una cadena:

Si formatea 1.toFixed (2) obtendría '1.00'.

Esto no es lo mismo que 1, ya que 1 no tiene 2 decimales.


Sé que JavaScript no es exactamente un lenguaje de rendimiento , pero es probable que obtenga un mejor rendimiento para un redondeo si usa algo como: roundedValue = Math.round (valor * 100) * 0.01

Pyro
fuente
2

¿Porque su uso principal es mostrar números? Si desea redondear números, Math.round()úselo con los factores apropiados.

Christoph
fuente
pero muestra NÚMEROS, ¿no debería devolver un "número"?
Derek Adair
3
@Derek: Solo en la forma en que '42'es un número ... que no lo es. El hecho de que una cadena contenga solo dígitos no lo convierte en un número. Esto no es PHP. :-P
Chris Jester-Young
jajaja No es una cadena que contiene un número ... Es un número que se pasa a un método. El método toma un número y devuelve una cadena.
Derek Adair
@DerekAdair bien, pero un navegador no puede mostrar un Número, muestra cadenas, por lo tanto, la conversión.
Nick M
1

Aquí hay una versión un poco más funcional de la respuesta m93aproporcionada.

const toFixedNumber = (toFixTo = 2, base = 10) => num => {
  const pow = Math.pow(base, toFixTo)
  return +(Math.round(num * pow) / pow)
}

const oneNumber = 10.12323223

const result1 = toFixedNumber(2)(oneNumber) // 10.12
const result2 = toFixedNumber(3)(oneNumber) // 10.123

// or using pipeline-operator
const result3 = oneNumber |> toFixedNumber(2) // 10.12
Sartaj
fuente
es una función práctica, para el tipo indefinido no funciona, he agregado código para este caso; if (num! == undefined) {return + (Math.round (num * pow) / pow)} else {return 0; }
Dino Liu