数字输入错误地接受带小数位的浮点数
Number input falsely accepting floats with decimal places
<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 位小数。
为防止这种情况发生,我建议您限制用户的操作:
我添加了 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, '');
});
<input id="my-input" type="text" decimals="2" placeholder="2 dec" />
虽然这肯定是您拥有的数字的错误,但它实际上可以防止其他 steps/values 组出现问题。分数舍入存在核心问题,使得完美的步骤验证并不那么容易,至少在那种精度水平下是这样。事实上,如果你查看 Firefox 源代码,你可以找到这条评论 https://dxr.mozilla.org/mozilla-central/source/dom/html/HTMLInputElement.cpp#4654:
There could be rounding issues below when dealing with fractional numbers, but let's ignore that until ECMAScript supplies us with a decimal number type.
所以问题不仅仅在于 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 等浏览器中,这正是发生的情况。一些 values/steps 对在应该验证的时候没有验证。
以步长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。后台发生的事情有点复杂和精确,但是处理小数计算的问题导致了这些问题,并且使得它们在每种情况下都难以预防。
只是一个简单的回答计算机使用二进制,而不是十进制所以计算机上的浮点计算与现实世界不同,这就是它显示意外结果的原因.
你可以用乘法和除法来修正错误。这将修复错误:
(0.1*100+0.2*100)/100
而且这不是 chrome 错误
<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 位小数。
为防止这种情况发生,我建议您限制用户的操作:
我添加了 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, '');
});
<input id="my-input" type="text" decimals="2" placeholder="2 dec" />
虽然这肯定是您拥有的数字的错误,但它实际上可以防止其他 steps/values 组出现问题。分数舍入存在核心问题,使得完美的步骤验证并不那么容易,至少在那种精度水平下是这样。事实上,如果你查看 Firefox 源代码,你可以找到这条评论 https://dxr.mozilla.org/mozilla-central/source/dom/html/HTMLInputElement.cpp#4654:
There could be rounding issues below when dealing with fractional numbers, but let's ignore that until ECMAScript supplies us with a decimal number type.
所以问题不仅仅在于 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 等浏览器中,这正是发生的情况。一些 values/steps 对在应该验证的时候没有验证。
以步长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。后台发生的事情有点复杂和精确,但是处理小数计算的问题导致了这些问题,并且使得它们在每种情况下都难以预防。
只是一个简单的回答计算机使用二进制,而不是十进制所以计算机上的浮点计算与现实世界不同,这就是它显示意外结果的原因.
你可以用乘法和除法来修正错误。这将修复错误:
(0.1*100+0.2*100)/100
而且这不是 chrome 错误