Работа с точностью с плавающей запятой в Javascript

У меня есть большое количество числовых значений y в javascript. Я хочу сгруппировать их, округляя их до ближайшего кратного x, и преобразовать результат в строку.

Как мне обойти раздражающую точность с плавающей запятой?

Например:

0.2 + 0.4 = 0.6000000000000001

Я пробовал две вещи:

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

и:

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

person Jeroen    schedule 27.07.2012    source источник
comment
На самом деле это нормальное поведение для double, вы просто не видите его в операторах печати на большинстве языков. Вы пробовали округлять числа?   -  person Vatev    schedule 28.07.2012
comment
Вы не можете обойтись без этого, поскольку это неотъемлемый аспект двоичных математических систем с плавающей запятой. Очевидно, это верно как для ваших значений x, так и для ваших значений y; если x равен 0,3, это невозможно представить точно. Округление до произвольных дробей приведет к неточности.   -  person Pointy    schedule 28.07.2012
comment
Итак, что было бы альтернативным способом преобразования y в "1.2".   -  person Jeroen    schedule 28.07.2012
comment
@Jeroen Я уверен, что он у тебя уже есть, но для протокола, Math.floor(y).   -  person pilau    schedule 05.08.2014
comment
@pilau, что приведет к 1, а не 1,2   -  person Josh1billion    schedule 29.02.2016


Ответы (5)


Из этого сообщения: Как справиться с точностью чисел с плавающей запятой в JavaScript? < / а>

У вас есть несколько вариантов:

person Rusty Fausak    schedule 27.07.2012
comment
Преобразуйте все свои числа в целые числа, я задумался об этом. Как мы знаем, JavaScript имеет один числовой тип Number, число с плавающей запятой IEEE 754. Если это так, то почему преобразование числа с плавающей запятой в целое число работает (и это так)? Действительно ли в JavaScript есть целочисленный тип данных, который просто недоступен через зарезервированное слово? - person Karl; 23.12.2014
comment
IEEE 754 может точно представлять целые числа до 2 ^ 50. Итак, если вы работаете в известном диапазоне, вы можете масштабировать свои значения, чтобы воспользоваться преимуществами 50-битной (или любой другой) точности, вместо того, чтобы тратить впустую точность, обычно зарезервированную для больших чисел. - person rich remer; 08.12.2015
comment
@richremer с плавающей запятой обладает тем свойством, что точность (для нормальных значений) не зависит от размера. - Итак, преобразовываете ли вы его в целые числа (например, умножая значения на некоторую константу), точность одинакова. - person paul23; 08.12.2017
comment
Количество значащих цифр - это количество цифр в экспоненциальном представлении (без начальных и конечных нулей), и выбор зависит от обстоятельств. - Аргумент toPrecision представляется числом значащих цифр. - toFixed - количество завершающих цифр. - person Rivenfall; 15.07.2020
comment
toFixed () преобразует число в строку. - person jefelewis; 03.12.2020

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

Быстрое решение:

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

Вы можете проверить полное объяснение здесь.

person Hamza Alayed    schedule 19.03.2014
comment
Не за что :-) - person peterh; 05.12.2017
comment
Math.m (0,07, 100) = ›7,000000000000002 - person joe cool; 25.04.2019
comment
Это не работает! - person shaiis.com; 29.12.2020

Вы можете сделать что-то вроде этого:

> +(Math.floor(y/x)*x).toFixed(15);
1.2
person philipvr    schedule 27.07.2012
comment
Не работает для всех комбинаций y и x. - person Jeroen; 28.07.2012
comment
toFixed возвращает строку - person dopatraman; 29.08.2018
comment
замените пол на раунд, + на parseFloat и 15 на меньшее число, если можете (аргумент toFixed может быть вычислен на основе x) - person Rivenfall; 15.07.2020

Посмотрите эту ссылку .. Мне это очень помогло.

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

Функция toPrecision(no_of_digits_required) возвращает string, поэтому не забудьте использовать функцию parseFloat() для преобразования в десятичную точку требуемой точности.

person prahaladp    schedule 09.04.2013
comment
Если вы оказались тем, кто проголосовал против меня за этот ответ, не могли бы вы объяснить, почему? (предоставленное решение, похоже, работает) - person prahaladp; 07.03.2018
comment
В основном проблема в том, что у вас есть js-числа y и x, и вам нужно ближайшее (от y) кратное x. Метод toPrecision не помогает, т.е. parseFloat((150).toPrecision(1)) === 200 - person Rivenfall; 15.07.2020

Решая эту задачу, я сначала нахожу количество десятичных знаков в x, а затем округляю y соответственно. Я бы использовал:

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

Он должен преобразовать x в строку, разделить ее по десятичной запятой, найти длину правой части, а затем y.toFixed(length) округлить y на основе этой длины.

person whaatt    schedule 27.07.2012
comment
Это немного проблематично, если x является целым числом. - person Jeroen; 28.07.2012