为什么模数运算符在javascript中返回小数?

时间:2010-10-19 08:26:04

标签: javascript floating-point modulus

为什么JavaScript中的49.90 % 0.10会返回0.09999999999999581?我预计它会是0。

7 个答案:

答案 0 :(得分:49)

因为JavaScript使用浮点数学,这总是会导致舍入错误。

如果您需要两位小数的精确结果,请在操作前将数字乘以100,然后再划分:

var result = ( 4990 % 10 ) / 100;

必要时回合。

答案 1 :(得分:17)

Javascript的Number使用“IEEE双精度”来存储值。它们无法准确存储所有十进制数。由于将十进制数转换为二进制数时的舍入误差,结果不为零。

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

因此,楼层(49.90 / 0.10)仅为498,其余为0.09999 ....


您似乎正在使用数字存储金额。 Don't do this,因为浮点运算会传播并放大舍入误差。将数字存储为美分的数量。整数可以准确表示,4990 % 10将返回0。

答案 2 :(得分:12)

我将此处留待此处以供将来参考,但这是一个方便的函数,可以更精确地处理涉及浮动的Remainder(因为JS doesn't have a modulo operator)。

  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>
&#13;
&#13;
&#13;

答案 3 :(得分:2)

<强>原因

浮点数不能精确存储所有十进制值。因此,当使用浮点格式时,输入值将始终存在舍入误差。 输入上的错误当然会导致输出错误。 在离散函数或运算符的情况下,函数或运算符离散的点周围的输出可能存在很大差异。 modula运算符是离散的,你的情况显然是这个问题的一个例子。

浮点值的输入和输出

因此,在使用浮点变量时,您应该始终注意这一点。并且在显示之前,您应该始终对浮点数计算所需的输出进行格式化/调节 当仅使用连续函数和运算符时,通常会舍入到所需的精度(不要截断)。用于将浮点数转换为字符串的标准格式设置功能通常会为您执行此操作 要根据预期的输入精度和所需的输出精度获得正确的输出,您还应该

  • 将输入舍入到预期的精度,或确保不能以更高的精度输入任何值。
  • 在舍入/格式化之前向输出添加一个小值,该值小于或等于所需精度的1/4,并且大于输入和计算期间的舍入误差引起的最大预期误差。如果不可能,则所用数据类型的精度组合不足以为计算提供所需的输出精度。

这两件事情往往没有完成,在大多数情况下,由于没有这样做而导致的差异太小而不适合大多数用户,但我已经有一个项目,其中输出没有被用户接受校正。

离散函数或运算符(如modula)

当涉及离散运算符或函数时,可能需要额外的更正以确保输出符合预期。在舍入之前舍入并添加小的修正不能解决问题 在应用离散函数或运算符之后,可能需要对中间计算结果进行特殊检查/校正。

此问题的具体情况

在这种情况下,您希望输入具有一定的精度,因此可以校正输出以获得比所需精度小很多的舍入误差的影响。

如果我们说数据类型的精确度是e。
您的输入不会存储为您输入的值a和b,而是存储为*(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的错误。
mod为* 3e的可能误差添加了一个* e,因为减去了2个值,可能出现a * 3e和a * e的错误。
因此,您应检查总可能的错误a * 4e是否小于所需的精度,如果满足该条件且结果与b的差异不大于最大可能的错误,则可以安全地将其替换为0.

最好避免出现问题

通过使用数据类型(整数或定点格式)进行这样的计算来避免这些问题通常更有效,这样可以存储预期的输入而不会出现舍入错误。 例如,您不应该使用浮点值进行财务计算。

答案 4 :(得分:1)

http://en.wikipedia.org/wiki/Modulo_operation 不要生气modulo用于整数^^ 浮动值会出现一些错误。

答案 5 :(得分:1)

看看floating points及其缺点 - 像0.1这样的数字无法正确保存为浮点数,因此总会出现这样的问题。取你的数字* 10或* 100,然后用整数进行计算。

答案 6 :(得分:-3)

这不是一个完美的答案,但它有效。

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

你可以使用如下,

format_float_bug(4990 % 10);

因为下面的数字(49.89999999999999857891452848)前15个小数位就像是9999999