Почему оператор модуля возвращает дробное число в javascript?

Почему 49.90 % 0.10 в JavaScript возвращается 0.09999999999999581? Я ожидал, что это будет 0.

Ответ 1

Поскольку JavaScript использует математику с плавающей запятой, которая всегда приводит к ошибкам округления.

Если вам нужен точный результат с двумя десятичными знаками, умножьте свои номера с помощью 100 до операции, а затем снова разделите его:

var result = ( 4990 % 10 ) / 100;

При необходимости округлите.

Ответ 2

Javascript Number использует "двойную точность IEEE" для хранения значений. Они не способны точно хранить все десятичные числа. Результат не равен нулю из-за ошибки округления при преобразовании десятичного числа в двоичный.

49.90 = 49.89999999999999857891452848...
 0.10 =  0.10000000000000000555111512...

Таким образом, пол (49.90/0.10) составляет всего 498, а остаток будет 0.09999....


Кажется, что вы используете номера для хранения суммы долларов. Не делайте этого, поскольку операции с плавающей запятой распространяются и усиливают ошибку округления. Сохраните номер как количество центов вместо. Целое число может быть представлено точно, а 4990 % 10 будет возвращать 0.

Ответ 3

Я просто оставлю это здесь для справки в будущем, но вот удобная функция, которая может более точно обрабатывать Remainder (поскольку JS не имеет оператора modulo) с использованием float.

  function floatSafeRemainder(val, step){
    var valDecCount = (val.toString().split('.')[1] || '').length;
    var stepDecCount = (step.toString().split('.')[1] || '').length;
    var decCount = valDecCount > stepDecCount? valDecCount : stepDecCount;
    var valInt = parseInt(val.toFixed(decCount).replace('.',''));
    var stepInt = parseInt(step.toFixed(decCount).replace('.',''));
    return (valInt % stepInt) / Math.pow(10, decCount);
  }

$(function() {
  
  
  function floatSafeModulus(val, step) {
    var valDecCount = (val.toString().split('.')[1] || '').length;
    var stepDecCount = (step.toString().split('.')[1] || '').length;
    var decCount = valDecCount > stepDecCount ? valDecCount : stepDecCount;
    var valInt = parseInt(val.toFixed(decCount).replace('.', ''));
    var stepInt = parseInt(step.toFixed(decCount).replace('.', ''));
    return (valInt % stepInt) / Math.pow(10, decCount);
  }
  
  
  $("#form").submit(function(e) {
    e.preventDefault();
    var safe = 'Invalid';
    var normal = 'Invalid';
    var var1 = parseFloat($('#var1').val());
    var var2 = parseFloat($('#var2').val());
    if (!isNaN(var1) && !isNaN(var2)) {
      safe = floatSafeModulus(var1, var2);
      normal = var1 % var2
    }
    $('#safeResult').text(safe);
    $('#normalResult').text(normal);
  });
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<form id="form" novalidate>
  <div>
    <input type="number" id="var1">%
    <input type="number" id="var2">
  </div>
  <div>safe: <span id="safeResult"></span><div>
  <div>normal (%): <span id="normalResult"></span></div>
  <input type="submit" value="try it out">
</form>

Ответ 4

http://en.wikipedia.org/wiki/Modulo_operation Не сердитесь по модулю с целыми числами ^^ Таким образом, плавающие значения имеют некоторые ошибки.

Ответ 5

Взгляните на плавающие точки и его недостатки - число, подобное 0.1, не может быть правильно сохранено как с плавающей запятой, поэтому всегда будут такие проблемы. Возьмите ваши цифры * 10 или * 100 и выполните вычисления с целыми числами.

Ответ 6

Причина

Плавающая точка не может точно хранить все десятичные значения. Поэтому при использовании форматов с плавающей запятой всегда будут ошибки округления на входных значениях. Ошибки на входах, конечно, приводят к ошибкам на выходе. В случае дискретной функции или оператора может быть большая разница на выходе вокруг точки, где функция или оператор дискретны. Оператор модуляции дискретный, и ваш случай явно является примером этой проблемы.

Ввод и вывод значений с плавающей запятой

Таким образом, при использовании переменных с плавающей запятой вы всегда должны знать об этом. И любой вывод, который вы хотите от вычисления с помощью плавающих точек, всегда должен быть отформатирован/подготовлен до отображения с учетом этого. Когда используются только непрерывные функции и операторы, часто выполняется округление до нужной точности (не усекается). Стандартные функции форматирования, используемые для преобразования float в строку, обычно делают это за вас.
Чтобы иметь правильный вывод на основе ожидаемой точности входов и желаемой точности вывода, вы также должны

  • Круглые входы с ожидаемой точностью или убедитесь, что значения не могут быть введены с более высокой точностью.
  • Добавьте небольшое значение к выходу до округления/форматирования, которое меньше или равно 1/4 от требуемой точности и больше максимальной ожидаемой ошибки, вызванной ошибками округления на входе и во время вычисления. Если это невозможно, сочетание точности используемого типа данных недостаточно для доставки желаемой выходной точности для вашего расчета.

Эти две вещи часто не выполняются, и в большинстве случаев различия, вызванные не их выполнением, слишком малы, чтобы быть важными для большинства пользователей, но у меня уже был проект, в котором результат не был принят пользователями без этих исправлений.

Дискретные функции или операторы (например, модулы)

При использовании дискретных операторов или функций могут потребоваться дополнительные исправления, чтобы гарантировать, что вывод будет таким, как ожидалось. Округление и добавление небольших исправлений до округления не могут решить проблему.
Может потребоваться специальная проверка/коррекция промежуточных результатов расчета, сразу после применения дискретной функции или оператора.

Конкретный случай этого вопроса

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

Если мы говорим, что точность вашего типа данных - e.
Ваш ввод не будет сохранен как введенные значения a и b, но как a * (1 +/- e) и b * (1 +/- e)
Результат деления a * (1 +/- e) на b * (1 +/- e) приведет к (a/b) (1 +/- 2e).
Функция модуляции должна усекать результат и снова умножить его. Таким образом, результат будет (a/bb) (1 +/- 3e) = a (1 +/- 3e), что приведет к ошибке a * 3e.
Мод добавляет a * e к возможной ошибке a * 3e из-за вычитания 2 значений с возможными ошибками a * 3e и a * e.
Поэтому вы должны проверить, что общая возможная ошибка a * 4e меньше требуемой точности, и если это условие выполнено и результат отличается от b не более максимальной возможной ошибки, вы можете с уверенностью заменить его на 0.

Лучше избегать проблемы

Часто бывает полезно избежать этих проблем, используя типы данных (целочисленные или фиксированные точечные форматы) для таких расчетов, которые могут хранить ожидаемый ввод без ошибок округления. Примером этого является то, что вы не должны использовать значения с плавающей запятой для финансовых расчетов.

Ответ 7

Это не идеальный ответ, но он работает.

function format_float_bug(num)
{
   return parseFloat( num.toFixed(15) ); 
} 

вы можете использовать следующее:

format_float_bug(4990 % 10);

потому что ниже числа (49.89999999999999857891452848) первые 15 знаков после запятой, как 9999999