为什么在 JavaScript 中,49.90 % 0.10
返回的结果是 0.09999999999999581
? 我原以为它应该是 0。
为什么在 JavaScript 中,49.90 % 0.10
返回的结果是 0.09999999999999581
? 我原以为它应该是 0。
因为 JavaScript 使用浮点数运算,可能会导致舍入误差。
如果您需要精确的结果保留两位小数,可以在进行运算之前将数字乘以100
,然后再在操作后除以100
:
var result = ( 4990 % 10 ) / 100;
必要时进行四舍五入。
(40.55 * 100 %1000)/100
返回的是0.549999...而不是0.55。 - Hiroki我将在此留下此内容以供将来参考,但是这里有一个方便的函数,可以更精确地处理涉及浮点数的余数(因为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>
return (((valInt % stepInt) + stepInt) % stepInt) / Math.pow(10, decCount)
。 - JamesJavascript的Number类型使用“IEEE双精度”来存储值。它们不能精确地存储所有十进制数。由于将十进制数转换为二进制时存在舍入误差,因此结果并非零。
49.90 = 49.89999999999999857891452848...
0.10 = 0.10000000000000000555111512...
因此,floor(49.90/0.10)仅为498,余数将为0.09999 ...
看起来您正在使用数字来存储美元金额。 不要这样做,因为浮点运算会传播和放大舍入误差。相反,应该将金额存储为美分的数量。整数可以被精确表示,4990%10
将返回0。
原因
浮点数无法精确存储所有小数值。因此,使用浮点格式时,输入值始终会存在舍入误差。 输入的误差当然会导致输出的误差。对于离散函数或运算符,其在离散点周围的输出可能存在很大差异。模运算是一个离散操作,您的情况显然就是这个问题的一个例子。
浮点值的输入和输出
因此,在使用浮点变量时,您应该始终意识到这一点。任何使用浮点数进行计算的输出都应该在显示之前进行格式化/调整,以充分考虑这一点。
当仅使用连续函数和运算符时,将其四舍五入到所需精度通常就足够了(不要截断)。用于将浮点数转换为字符串的标准格式功能通常会自动完成此操作。
为了根据期望的输入精度和输出精度得出正确的输出,您还应该:
这两件事情通常被忽略,对于大多数用户来说,由于没有执行这些操作而导致的差异太小,不会对其产生重要影响,但我曾经遇到过一个项目,如果没有进行这些更正,用户将不接受输出结果。
离散函数或运算符(例如模运算)
当涉及离散运算符或函数时,可能需要额外的更正才能确保输出如预期。舍入并在四舍五入之前添加小的更正无法解决问题。
可能需要在应用离散函数或运算符后立即对中间计算结果进行特殊检查/更正。
此问题的具体情况
在此情况下,您期望输入具有一定的精度,因此可以通过纠正舍入误差的输出来解决问题,这种误差比所需的精度小得多。
如果我们说你的数据类型的精度是e。最好避免出现问题
通常更有效的方法是通过使用整数或固定点格式等可以存储预期输入而不会产生舍入误差的数据类型来进行此类计算。一个例子就是您永远不应该在财务计算中使用浮点值。let asBuffer = new ArrayBuffer(8);
let asInt = new Uint32Array(asBuffer );
let asFloat = new Float32Array(asBuffer );
asFloat.set([49.9,.1]);
asInt.set([asFloat[0]/asFloat[1], asFloat[0]%asFloat[1]]);
Result
asInt Uint32Array(2) [499, 0, buffer: ArrayBuffer(8), byteLength: 8, byteOffset: 0, length: 2, Symbol(Symbol.toStringTag): 'Uint32Array']
//In general I'm moving more and more towards
//Using typed arrays to store values in JS.
这不是一个完美的答案,但它可以工作。
function format_float_bug(num)
{
return parseFloat( num.toFixed(15) );
}
你可以按照以下方式使用:
format_float_bug(4990 % 10);
因为下面的数字(49.89999999999999857891452848)的前15位小数像是9999999