Tratar con precisión flotante en Javascript [duplicado]

Tengo una gran cantidad de valores numéricos. y en javascript. Quiero agruparlos redondeándolos al múltiplo más cercano de x y convertir el resultado en una cadena.

¿Cómo evito la molesta precisión de punto flotante?

Por ejemplo:

0.2 + 0.4 = 0.6000000000000001

Dos cosas que he probado:

>>> y = 1.23456789 
>>> x = 0.2 
>>> parseInt(Math.round(Math.floor(y/x))) * x; 
1.2000000000000002

y:

>>> y = 1.23456789 
>>> x = 0.2 
>>> y - (y % x)
1.2000000000000002

preguntado el 27 de julio de 12 a las 22:07

Esto es realmente un comportamiento normal para double simplemente no lo ve en declaraciones impresas en la mayoría de los idiomas. ¿Has probado a redondear tus números? -

Realmente no puede "eludirlo", ya que es un aspecto intrínseco de los sistemas matemáticos binarios de punto flotante. Eso es cierto para los valores de "x" y "y", aparentemente; si "x" es 0.3 eso no se puede representar exactamente. "Redondear" a fracciones arbitrarias resultará en imprecisión. -

Entonces, ¿cuál sería una forma alternativa de convertir y a "1.2". -

@Jeroen Estoy seguro de que ya lo tienes, pero solo para que conste, Math.floor(y). -

@pilau eso daría como resultado 1, no 1.2 -

5 Respuestas

De esta publicación: ¿Cómo lidiar con la precisión numérica de punto flotante en JavaScript?

Tienes pocas opciones:

  • Use un tipo de datos especial para decimales, como decimal.js
  • Formatee su resultado a un número fijo de dígitos significativos, como este: (Math.floor(y/x) * x).toFixed(2)
  • Convierte todos tus números a enteros

Respondido el 06 de junio de 18 a las 23:06

"Convierte todos tus números a enteros", me he preguntado sobre esto. Como sabemos, JavaScript tiene un tipo de número Number, un flotador IEEE 754. Si ese es el caso, entonces ¿por qué funciona la conversión de un flotante a un número entero (y lo hace)? ¿JavaScript realmente tiene un tipo de datos entero al que simplemente no se puede acceder a través de una palabra reservada? - Karl

IEEE 754 puede representar exactamente números enteros hasta algo así como 2^50. Entonces, si está trabajando dentro de un rango conocido, puede escalar sus valores para aprovechar los 50 bits (o lo que sea) de precisión, en lugar de desperdiciar la precisión normalmente reservada para números grandes. - rico remer

@richremer un punto flotante tiene la propiedad de que la precisión (para valores normales) no depende del tamaño. - Entonces, ya sea que lo convierta a números enteros (por ejemplo, multiplicando los valores con alguna constante), la precisión es igual. - paul23

El número de "dígitos significativos" es el número de dígitos que utilizan la notación científica (sin ceros al principio ni al final) y es una elección basada en las circunstancias. - El argumento de toPrecision parece ser un número de dígitos significativos. - toFixed es para un número de dígitos finales. - Rivenfall

toFixed() convertirá el número en una cadena. - jefelewis

Podrías hacer algo como esto:

> +(Math.floor(y/x)*x).toFixed(15);
1.2

Edit: Sería mejor usar big.js.

grande.js

Una biblioteca pequeña, rápida y fácil de usar para aritmética decimal de precisión arbitraria.

>> bigX = new Big(x)
>> bigY = new Big(y)
>> bigY.div(bigX).round().times(bigX).toNumber() // => 1.2

Respondido 05 Oct 21, 19:10

No funciona para todas las combinaciones. y y x. - Jeroen Ooms

toFixed devuelve una cadena - dopatraman

reemplazar piso por redondo, + por parseFloat y 15 por un número más bajo si puede (el argumento toFixed podría calcularse en función de x) - Rivenfall

> var x = 0.1
> var y = 0.2
> var cf = 10
> x * y
0.020000000000000004
> (x * cf) * (y * cf) / (cf * cf)
0.02

Solución rápida:

var _cf = (function() {
  function _shift(x) {
    var parts = x.toString().split('.');
    return (parts.length < 2) ? 1 : Math.pow(10, parts[1].length);
  }
  return function() { 
    return Array.prototype.reduce.call(arguments, function (prev, next) { return prev === undefined || next === undefined ? undefined : Math.max(prev, _shift (next)); }, -Infinity);
  };
})();

Math.a = function () {
  var f = _cf.apply(null, arguments); if(f === undefined) return undefined;
  function cb(x, y, i, o) { return x + f * y; }
  return Array.prototype.reduce.call(arguments, cb, 0) / f;
};

Math.s = function (l,r) { var f = _cf(l,r); return (l * f - r * f) / f; };

Math.m = function () {
  var f = _cf.apply(null, arguments);
  function cb(x, y, i, o) { return (x*f) * (y*f) / (f * f); }
  return Array.prototype.reduce.call(arguments, cb, 1);
};

Math.d = function (l,r) { var f = _cf(l,r); return (l * f) / (r * f); };

> Math.m(0.1, 0.2)
0.02

Puedes consultar la explicación completa aquí.

Respondido el 04 de diciembre de 17 a las 14:12

Matemáticas.m(0.07, 100) => 7.000000000000002 - Joe fresco

¡Esto no está funcionando! - shaiis.com

Echa un vistazo a este enlace .. Me ayudó mucho.

http://www.w3schools.com/jsref/jsref_toprecision.asp

La opción toPrecision(no_of_digits_required) la función devuelve un string así que no olvides usar el parseFloat() función para convertir al punto decimal de la precisión requerida.

Respondido 01 Jul 19, 12:07

Si resultó ser alguien que me votó negativo por esta respuesta, ¿podría explicar por qué? (la solución provista parece funcionar) - prahaladp

Básicamente, el problema es este, tienes números js y y x y quieres el múltiplo de x más cercano (de y). El método toPrecision no ayuda, es decir parseFloat((150).toPrecision(1)) === 200 - Rivenfall

hacer una conversión de análisis de cadena/flotante para un problema de redondeo no es exactamente lo que yo llamaría realizar a escala: D (mejor: implementar/usar una función toPrecision que opera/devuelve únicamente en números!) - Chakabam

Al abordar esta tarea, primero encontraría el número de lugares decimales en x, luego redondo y respectivamente. Yo usaría:

y.toFixed(x.toString().split(".")[1].length);

debería convertir x a una cadena, divídala sobre el punto decimal, encuentre la longitud de la parte derecha y luego y.toFixed(length) debería redondear y en base a esa longitud.

Respondido 27 Jul 12, 22:07

Esto se vuelve un poco problemático de x es un entero. - Jeroen Ooms

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.