Почему мой ввод HTML-номера застревает на шаге 0.010000000000000009?

#html #html5-input-number

Вопрос:

Когда я создаю HTML <input type="number"/> с step="0.010000000000000009" помощью . Я могу нажать кнопку, чтобы увеличить значение только один раз, чтобы заполнить min значение, а затем только один раз для первого увеличения на значение шага, затем оно застревает на этом значении, и дальнейшие щелчки не имеют никакого эффекта. Я могу воспроизвести это только с min="1.2" max="1.3" помощью .

Все остальные комбинации min , max и step я попытался разрешить нажимать кнопку несколько раз, пока она не достигнет максимума в определенных шагах.

 <!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <title>input with decimal step</title>
  </head>
  <body>
    <input type="number" min="1.2" max="1.3" step="0.010000000000000009"/>
  </body>
</html> 

Комментарии:

1. Firefox позволяет вам дважды щелкнуть, прежде чем застрять.

2. Никакого упрека в Хроме. Если это только FF, то сообщение об ошибке может быть оправдано.

3. Мне приходит на ум арифметика с плавающей запятой.

4. @Dominik Я был недостаточно точен в своем вопросе, отредактированном

5. Я не уверен, что это обязательно ошибка в Firefox, скорее, Chrome более снисходителен. Согласно MDN, max значения, если они указаны, должны быть равны min ( n * step ) — если я правильно читаю их (не совсем ясные) замечания: developer.mozilla.org/en-US/docs/Web/HTML/Attributes/step

Ответ №1:

Я считаю, что, согласно спецификации HTML5 Living, ваш <input /> элемент «страдает от несоответствия шагов».:

Следуя спецификациям вашего дела…

  • step Значение равно 0.010000000000000009 (от parseFloat ).
  • step-scale-factor Значение всегда 1 для <input type="number" />
  • Это тот step-base min атрибут, который есть 1.2 .
  • То allowed-value-step есть step * step-scale-factor то, что есть 1 * 0.010000000000000009 === 0.010000000000000009 .
  • Re: «Проверка ограничений», в спецификации говорится следующее:

    Проверка ограничений: Когда у элемента есть allowed-value-step , и результат применения алгоритма преобразования строки в число к строке, заданной элементом value="" , является числом, и это число, вычитаемое из step-base , не является целым кратным allowed-value-step , элемент страдает от несоответствия шага.

    Я перефразирую это так…:

    • Если элемент имеет allowed-value-step числовой value="" атрибут и.
      • (Так что это только после <input/> того, как он не пуст, например, один раз нажмите кнопки вверх/вниз, чтобы присвоить ему значение 1.2 )
      • …затем вычитаем 1.2 (то step-base ) из 1.2 (то value="" ), чтобы дать 0 .
      • И 0 не является целым кратным от 0.010000000000000009 .
      • Следовательно, элемент страдает от несоответствия шага.




Теперь, с вашими выводами:

Я могу только нажать кнопку, чтобы увеличить значение один раз, чтобы заполнить min значение

Да, это следует спецификации HTML5: начальное value="" значение пусто, поэтому браузеры будут выбирать следующее допустимое наибольшее значение, которое является min="1.2" значением.

и затем только один раз для первого увеличения на значение шага

Да, это потому , что следующий шаг 1.2 0.010000000000000009 1.210000000000000009 — это то, к чему оба браузера обрезают отображение 1.21 , однако, как только это происходит, мы видим разницу в поведении браузера:

  • Firefox, похоже, правильно соответствует спецификации, и он устанавливает HTMLInputElement.validity.stepMismatch true и отключает кнопки вверх/вниз из-за несоответствия шага.
  • Chrome 92 этого не делает, и использование кнопок вверх/вниз для установки других значений все равно приводит к HTMLInputElement.validity.stepMismatch === false .

Вот фрагмент, который показывает информацию о достоверности:

 <!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <title>input with decimal step</title>
  </head>
  <body>
    <input id="inp" type="number" min="1.2" max="1.3" step="0.010000000000000009" />

    <ul id="eventsList"></ul>

    <script>
    const inp = document.getElementById('inp');
    const ul  = document.getElementById('eventsList');

    inp.addEventListener( 'input', logInfo );
    inp.addEventListener( 'change', logInfo );
    inp.addEventListener( 'click', logInfo );

    function logInfo( e ) {
        const li = document.createElement('li');
        li.textContent = e.type   ". Value: "   inp.value   ", valid: "   inp.validity.valid   ", stepMismatch: "   inp.validity.stepMismatch;
        ul.appendChild( li );
    }
    </script>

  </body>
</html> 
  • В Chrome он позволяет мне нажать кнопку «Вверх» 10 раз ( 1.2 , 1.21 , 1.22 , 1.23 , …, 1.28 , до 1.29 ), прежде чем он остановится, хотя он никогда не сообщает stepIsmatch: true .
  • В Firefox это позволяет мне нажать кнопку «Вверх» 1 раз ( 1.2 и 1.21 ), прежде чем она установится stepMismatch: true .

Мои деньги на том, что Chrome делает свое дело, учитывая, что их интерпретация (или, по крайней мере, их реализация) спецификации более снисходительна и с меньшей вероятностью приведет к разочарованию конечного пользователя, когда step установлено странное значение.


Что интересно, так это то, что Number тип в браузерах, похоже, способен точно представлять 0.010000000000000009 без каких-либо контрольных округлений IEEE 754 (то же самое с Python), в то время как типы C#/.NET Single (32-разрядные) и Double (64-разрядные) с плавающей запятой и подавляют, и дают мне довольно плохие приближения.