为什么模数运算符在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>
http://en.wikipedia.org/wiki/Modulo_operation不要生气modulo用于整数;^^所以浮动值出现一些错误。
看看浮点数和它的缺点 – 像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