数字输入错误地接受带小数位的浮点数

时间:2019-09-16 22:30:50

标签: html chromium

<form>
  <input type="number" step="1" required>
  <input type="submit">
</form>

在上面的代码段中,如果您插入

0.0000001

在数字输入中,您不能提交表单:“ step”属性可以防止这种情况。

但是如果您插入

0.00000001

它可以在Chrome / Chromium中使用!为什么在验证输入时似乎只读取前七个小数位?是否在任何地方对此进行了记录,我该怎么做以防止这种情况发生?

我测试了Firefox,它不接受任何此类值。

为澄清起见,common pitfall 0.30000000000000004 === 0.1+0.2在这里不是问题,因为只有16位小数会发生。上面的输入值失败,保留8位小数。

3 个答案:

答案 0 :(得分:3)

为防止这种情况,我建议您限制用户的操作:

我添加了onkeypress="return event.charCode >= 48 && event.charCode <= 57",以禁止用户键入.,

最后一个问题是用户可以在输入中粘贴值,因此有一个小脚本可以防止这种情况:

  const pasteBox = document.getElementById("no-paste");
  pasteBox.onpaste = e => {
    e.preventDefault();
    return false;
  };
<form>
  <input 
     type="number" 
     step="1" 
     onkeypress="return event.charCode >= 48 && event.charCode <= 57" 
     required
     id="no-paste">
  <input type="submit">
</form>

希望它对您有所帮助;)

编辑:您可以使用此javascript来限制小数位数:

const myInput = document.getElementById("my-input");
  myInput.addEventListener("input", function() {
    const dec = myInput.getAttribute('decimals');
    const regex = new RegExp("(\\.\\d{" + dec + "})\\d+", "g");
    myInput.value = myInput.value.replace(regex, '$1');
  });
 <input id="my-input" type="text" decimals="2" placeholder="2 dec" />

答案 1 :(得分:2)

虽然这肯定是您所拥有的数字的错误,但实际上可以防止其他步骤/值组出现问题。小数舍入有一些核心问题,要想进行完美的步骤验证就不那么容易了,至少没有那么精确。实际上,如果您查看Firefox源代码,则可以找到以下注释https://dxr.mozilla.org/mozilla-central/source/dom/html/HTMLInputElement.cpp#4654

  

在处理小数时,下面可能会出现舍入问题,但是在ECMAScript为我们提供十进制数类型之前,我们可以忽略它。

因此,问题不仅仅与Chrome有关。但是,让我们从您描述的错误开始。如果您查看Chromium源代码,则可以看到在step_range处理中,有一个名为 AcceptableError 的方法,该方法可以调整该值和针对该步骤调整的值之间的差。参见https://cs.chromium.org/chromium/src/third_party/blink/renderer/core/html/forms/step_range.cc?l=60。此可接受的错误定义为:

the step value / 2^24 (in the source code UINT64_C(1) << FLT_MANT_DIG) 

步骤1:

1 / 16777216 = 0.000000059604645

因此,在您的原始示例中,即使该数字没有余数,该数字也将有效。计算余数的公式是:

value - step * Math.abs(Math.round(value / step));

例如,如果您输入2.000000059604644,则余数将为:

  2.000000059604644 - 1 * Math.abs(Math.round(2.000000059604644))
= 2.000000059604644 - 2
= 0.000000059604644 

0.000000059604644低于0.000000059604645,因此它将进行验证。 2.000000059604646不会。

<form>
  <input type="number" step="1" required>
  <input type="submit">
</form>

您可以尝试使用更大的数字,例如,以167772167为步长。这将产生一个可接受的错误:

16777217 / 16777216 = 1.000000059604645

,其值为16777218的余数为

  16777218 - 16777217 * Math.abs(Math.round(1.000000059604645 ))
= 16777218 - 16777217
= 1

<form>
  <input type="number" step="16777217" required>
  <input type="submit">
</form>

在您可接受的错误1下,因此它将验证。

所以这是错误。它允许复仇的目的是容忍某些步骤和值,这些步骤和值由于十进制运算的精度损失而无法在应有的时间进行验证。而在Firefox之类的浏览器中,这正是发生的情况。某些值/步骤对在需要时不生效。

例如,采取853.2394的步长和495714162280.48785的值。这肯定可以验证,因为:

495714162280.48785 / 853.2394 = 580978987

如果您在Firefox中尝试过,它将无法验证。但是在Chrome中,由于第一种情况下会导致错误的容忍度,因此它可以进行验证。

<form>
  <input type="number" step="853.2394" required>
  <input type="submit">
</form>

因此,最后,此错误与小数运算的已知问题有关,这并不精确。经典示例是0.1 + 0.2 = 0.30000000000000004。后台发生的事情更加复杂和精确,但是处理小数计算的问题会导致这些问题,并使每种情况都难以避免。

答案 2 :(得分:1)

一个简单的答案计算机工作于二进制文件,而不是十进制,因此计算机上的浮点计算与真实世界不同,这就是为什么它显示出意外结果的原因。您可以使用乘除法纠正错误。这将解决错误。

(0.1*100+0.2*100)/100

这不是Chrome错误