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

почему 49.90 % 0.10 в JavaScript return 0.09999999999999581? Я ожидал, что будет 0.

7 ответов


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

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

var result = ( 4990 % 10 ) / 100;

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


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

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

таким образом, этаж(49.90 / 0.10) составляет всего 498, а остальная часть будет 0.09999....


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


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

  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>

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


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


причина

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

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

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

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

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

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

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

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

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

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

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

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


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

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

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

format_float_bug(4990 % 10);

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