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

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

因为JavaScript使用总是导致舍入错误的浮点math。

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

 var result = ( 4990 % 10 ) / 100; 

如果有必要的话

JavaScript的数字是使用“IEEE双精度”来存储的价值。 它们不能精确地存储所有的十进制数字。 将十进制数转换为二进制时由于四舍五入错误,结果不为零。

 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> 

看看浮点数和它的缺点 – 像0.1这样的数字不能被正确保存为浮点数,所以总会有这样的问题。 把你的数字* 10或* 100,并用整数进行计算。

原因

浮点不能完全保存所有十进制值。 所以当使用浮点格式时,input值总会有舍入误差。 input上的错误会导致输出上的错误。 在离散function或操作员的情况下,function或操作员离散点的输出可能会有很大差异。 模数运算符是离散的,你的情况显然是这个问题的一个例子。

input和输出浮点值

所以,当使用浮点variables时,您应该始终注意到这一点。 无论你想从浮点计算中得到什么输出,都应该在显示之前进行格式化/调节。
当只使用连续函数和运算符时,往往会舍入到所需的精度(不要截断)。 用于将浮动转换为string的标准格式化function通常会为您执行此操作。
要根据input的期望精度和期望的输出精度得到正确的输出,你也应该这样做

  • 将input舍入到预期的精确度,或确保没有数值可以更高的精度input。
  • 在对输出进行舍入/格式化之前,在输出中添加一个较小的值,该值小于或等于所需精度的1/4,并且大于input和计算过程中舍入误差导致的最大预期误差。 如果这是不可能的,则所使用的数据types的精度的组合不足以为计算提供期望的输出精度。

这两件事往往没有完成,在大多数情况下,不做这些事情造成的差异太小了,对于大多数用户来说很重要,但是我已经有一个项目,没有这些更正的用户不接受输出。

离散function或运算符(如modula)

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

这个问题的具体情况

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

如果我们说你的数据types的精度是e。
您的input不会被存储为您input的值a和b,而是以*(1 +/- e)和b *(1 +/- e)
a *(1 +/- e)除以b *(1 +/- e)的结果将导致(a / b) (1 +/- 2e)。
模块函数必须截断结果并再次乘。 所以结果是(a / b b)(1 +/- 3e)= a(1 +/- 3e),导致a * 3e的误差。
由于可能的a * 3e和a * e的误差减去2个值,所以mod将a * e加到a * 3e的可能的误差上。
因此,您应该检查a * 4e的总体可能误差是否小于所需的精度,如果满足该条件且结果与b不同,则可以安全地将其replace为0。

最好避免出现问题

通过使用数据types(整数或固定点格式)来避免这些问题通常会更有效率,这样可以存储预期的input而不会产生舍入误差。 其中的一个例子是,您不应该使用浮点值进行财务计算。

这不是一个完美的答案,但它的工作原理。

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

你可以使用如下,

 format_float_bug(4990 % 10); 

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