78 votos

Truncar (no redondear) números decimales en javascript

Estoy tratando de truncar los números decimales a los decimales. Algo así:

5.467   -> 5.46  
985.943 -> 985.94

toFixed(2) hace lo justo pero redondea el valor. No necesito redondear el valor. Espero que esto sea posible en javascript.

55voto

Nick Knowlson Puntos 2715

La respuesta de Dogbert es buena, pero si tu código tiene que lidiar con números negativos, Math.floor por sí mismo puede dar resultados inesperados.

Por ejemplo Math.floor(4.3) = 4 pero Math.floor(-4.3) = -5

Utiliza una función de ayuda como ésta para obtener resultados consistentes:

truncateDecimals = function (number) {
    return Math[number < 0 ? 'ceil' : 'floor'](number);
};

// Applied to Dogbert's answer:
var a = 5.467;
var truncated = truncateDecimals(a * 100) / 100; // = 5.46

Aquí hay una versión más conveniente de esta función:

truncateDecimals = function (number, digits) {
    var multiplier = Math.pow(10, digits),
        adjustedNum = number * multiplier,
        truncatedNum = Math[adjustedNum < 0 ? 'ceil' : 'floor'](adjustedNum);

    return truncatedNum / multiplier;
};

// Usage:
var a = 5.467;
var truncated = truncateDecimals(a, 2); // = 5.46

// Negative digits:
var b = 4235.24;
var truncated = truncateDecimals(b, -2); // = 4200

Si no es el comportamiento deseado, inserte una llamada a Math.abs en la primera línea:

var multiplier = Math.pow(10, Math.abs(digits)),

EDITAR: shendz señala correctamente que el uso de esta solución con a = 17.56 producirá incorrectamente 17.55 . Para saber más sobre por qué ocurre esto, lea Lo que todo informático debe saber sobre la aritmética en coma flotante . Por desgracia, escribir una solución que elimine todas las fuentes de error de punto flotante es bastante complicado con javascript. En otro lenguaje usarías enteros o quizás un tipo Decimal, pero con javascript...

Esta solución debe ser 100% preciso, pero también será más lento:

function truncateDecimals (num, digits) {
    var numS = num.toString(),
        decPos = numS.indexOf('.'),
        substrLength = decPos == -1 ? numS.length : 1 + decPos + digits,
        trimmedResult = numS.substr(0, substrLength),
        finalResult = isNaN(trimmedResult) ? 0 : trimmedResult;

    return parseFloat(finalResult);
}

Para los que necesitan velocidad pero también quieren evitar los errores de punto flotante, pruebe algo como BigDecimal.JS . Puede encontrar otras bibliotecas javascript BigDecimal en esta pregunta SO: "¿Existe una buena biblioteca BigDecimal de Javascript?" y aquí hay una buena entrada en el blog sobre bibliotecas matemáticas para Javascript

47voto

kirilloid Puntos 7139

upd :

Así que, después de todo, los fallos de redondeo siempre te perseguirán, por mucho que intentes compensarlos. De ahí que se deba atacar el problema representando los números exactamente en notación decimal.

Number.prototype.toFixedDown = function(digits) {
    var re = new RegExp("(\\d+\\.\\d{" + digits + "})(\\d)"),
        m = this.toString().match(re);
    return m ? parseFloat(m[1]) : this.valueOf();
};

[   5.467.toFixedDown(2),
    985.943.toFixedDown(2),
    17.56.toFixedDown(2),
    (0).toFixedDown(1),
    1.11.toFixedDown(1) + 22];

// [5.46, 985.94, 17.56, 0, 23.1]

Solución antigua, propensa a errores, basada en la recopilación de otras:

Number.prototype.toFixedDown = function(digits) {
  var n = this - Math.pow(10, -digits)/2;
  n += n / Math.pow(2, 53); // added 1360765523: 17.56.toFixedDown(2) === "17.56"
  return n.toFixed(digits);
}

32voto

Dogbert Puntos 44003
var a = 5.467;
var truncated = Math.floor(a * 100) / 100; // = 5.46

21voto

RichardTheKiwi Puntos 58121

Puedes arreglar el redondeo restando 0,5 para toFixed, por ejemplo

(f - 0.005).toFixed(2)

17voto

ruffin Puntos 1906

Considere aprovechando la doble tilde: ~~ .

Toma el número. Multiplica por las cifras significativas después del decimal para poder truncar a cero lugares con ~~ . Divide ese multiplicador de nuevo. Ganancia.

function truncator(numToTruncate, intDecimalPlaces) {    
    var numPower = Math.pow(10, intDecimalPlaces); // "numPowerConverter" might be better
    return ~~(numToTruncate * numPower)/numPower;
}

Estoy tratando de resistirme a envolver el ~~ llamada entre paréntesis; el orden de las operaciones debería hacer que funcione correctamente, creo.

alert(truncator(5.1231231, 1)); // is 5.1

alert(truncator(-5.73, 1)); // is -5.7

alert(truncator(-5.73, 0)); // is -5

Enlace de JSFiddle .

EDITAR: Mirando hacia atrás, involuntariamente también he manejado casos para redondear a la izquierda del decimal también.

alert(truncator(4343.123, -2)); // gives 4300.

La lógica es un poco loca para ese uso, y puede beneficiarse de una rápida refactorización. Pero aún así funciona. Mejor suerte que bien.

Iteramos.com

Iteramos es una comunidad de desarrolladores que busca expandir el conocimiento de la programación mas allá del inglés.
Tenemos una gran cantidad de contenido, y también puedes hacer tus propias preguntas o resolver las de los demás.

Powered by:

X