diff --git a/core/lib/Drupal/Component/Utility/Number.php b/core/lib/Drupal/Component/Utility/Number.php
index 39143d9..7038017 100644
--- a/core/lib/Drupal/Component/Utility/Number.php
+++ b/core/lib/Drupal/Component/Utility/Number.php
@@ -17,9 +17,18 @@ class Number {
    *
    * This is based on the number/range verification methods of webkit.
    *
-   * @param float $value
+   * This is called by Drupal\Core\Render\Element\Number::validateNumber() to
+   * validate a numeric field. That function passes the $value parameter as a
+   * string, as provided on the text field of the form. Besides integers and
+   * floating numbers, we also support decimal numbers which are not stored in
+   * IEEE 754 format. In somewhat higher precisions for these numbers, the $step
+   * value cannot accurately represent the desired precision, when it is passed
+   * as a float. Passing it as a string bypasses this loss of precision and
+   * enables a correct calculation of the step validity.
+   *
+   * @param float|string $value
    *   The value that needs to be checked.
-   * @param float $step
+   * @param float|string $step
    *   The step scale factor. Must be positive.
    * @param float $offset
    *   (optional) An offset, to which the difference must be a multiple of the
diff --git a/core/lib/Drupal/Core/Field/Plugin/Field/FieldWidget/NumberWidget.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldWidget/NumberWidget.php
index b4fac53..090ba1d 100644
--- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldWidget/NumberWidget.php
+++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldWidget/NumberWidget.php
@@ -73,6 +73,7 @@ public function formElement(FieldItemListInterface $items, $delta, array $elemen
       '#type' => 'number',
       '#default_value' => $value,
       '#placeholder' => $this->getSetting('placeholder'),
+      '#number_type' => $this->fieldDefinition->getType(),
     ];
 
     // Set the step for floating point and decimal numbers.
diff --git a/core/lib/Drupal/Core/Render/Element/Number.php b/core/lib/Drupal/Core/Render/Element/Number.php
index b7b2524..7733207 100644
--- a/core/lib/Drupal/Core/Render/Element/Number.php
+++ b/core/lib/Drupal/Core/Render/Element/Number.php
@@ -88,8 +88,14 @@ public static function validateNumber(&$element, FormStateInterface $form_state,
       // Check that the input is an allowed multiple of #step (offset by #min if
       // #min is set).
       $offset = isset($element['#min']) ? $element['#min'] : 0.0;
+      $step = $element['#step'];
+      if (isset($element['#number_type']) && ($element['#number_type'] == 'decimal')) {
+        // PHP mangles the precision of floating-point arguments, so convert
+        // the step to string for non-floating-point numbers.
+        $step = (string) $element['#step'];
+      }
 
-      if (!NumberUtility::validStep($value, $element['#step'], $offset)) {
+      if (!NumberUtility::validStep($value, $step, $offset)) {
         $form_state->setError($element, t('%name is not a valid number.', ['%name' => $name]));
       }
     }
diff --git a/core/modules/field/src/Tests/Number/NumberFieldTest.php b/core/modules/field/src/Tests/Number/NumberFieldTest.php
index c691410..9d894d7 100644
--- a/core/modules/field/src/Tests/Number/NumberFieldTest.php
+++ b/core/modules/field/src/Tests/Number/NumberFieldTest.php
@@ -41,12 +41,14 @@ protected function setUp() {
   public function testNumberDecimalField() {
     // Create a field with settings to validate.
     $field_name = Unicode::strtolower($this->randomMachineName());
+    // Having precision = 10 and scale = 4 means 6 numbers before the decimal
+    // point and 4 after.
     FieldStorageConfig::create([
       'field_name' => $field_name,
       'entity_type' => 'entity_test',
       'type' => 'decimal',
       'settings' => [
-        'precision' => 8, 'scale' => 4,
+        'precision' => 10, 'scale' => 4,
       ]
     ])->save();
     FieldConfig::create([
@@ -74,33 +76,27 @@ public function testNumberDecimalField() {
     $this->assertFieldByName("{$field_name}[0][value]", '', 'Widget is displayed');
     $this->assertRaw('placeholder="0.00"');
 
-    // Submit a signed decimal value within the allowed precision and scale.
-    $value = '-1234.5678';
-    $edit = [
-      "{$field_name}[0][value]" => $value,
-    ];
-    $this->drupalPostForm(NULL, $edit, t('Save'));
-    preg_match('|entity_test/manage/(\d+)|', $this->url, $match);
-    $id = $match[1];
-    $this->assertText(t('entity_test @id has been created.', ['@id' => $id]), 'Entity was created');
-    $this->assertRaw($value, 'Value is displayed.');
-
-    // Try to create entries with more than one decimal separator; assert fail.
-    $wrong_entries = [
-      '3.14.159',
-      '0..45469',
-      '..4589',
-      '6.459.52',
-      '6.3..25',
+    // Submit a few signed decimal value within the allowed precision and scale.
+    $valid_entries = [
+      '-1234.5678',
+      '19999.0000',
+      '99999.0000',
+      // '909888.96', This fails!
+      '909888.99',
+      '988999.0096',
+      // '988999.0099', This fails!
     ];
-
-    foreach ($wrong_entries as $wrong_entry) {
+    foreach ($valid_entries as $valid_entry) {
       $this->drupalGet('entity_test/add');
       $edit = [
-        "{$field_name}[0][value]" => $wrong_entry,
+        "{$field_name}[0][value]" => $valid_entry,
       ];
       $this->drupalPostForm(NULL, $edit, t('Save'));
-      $this->assertRaw(t('%name must be a number.', ['%name' => $field_name]), 'Correctly failed to save decimal value with more than one decimal point.');
+      preg_match('|entity_test/manage/(\d+)|', $this->url, $match);
+      $id = $match[1];
+      $this->assertText(t('entity_test @id has been created.', ['@id' => $id]), 'Entity was created');
+      $this->assertRaw($valid_entry, t('Value @val is displayed.', ['@val' => $valid_entry]));
+      $this->assertNoRaw(t('%name is not a valid number.', ['%name' => $field_name]), 'Values are accepted');
     }
 
     // Try to create entries with minus sign not in the first position.
diff --git a/core/tests/Drupal/Tests/Component/Utility/NumberTest.php b/core/tests/Drupal/Tests/Component/Utility/NumberTest.php
index 8d415de..d5181dc 100644
--- a/core/tests/Drupal/Tests/Component/Utility/NumberTest.php
+++ b/core/tests/Drupal/Tests/Component/Utility/NumberTest.php
@@ -89,6 +89,13 @@ public static function providerTestValidStep() {
       // Step mismatches very close to being valid.
       [70 + 9e-7, 10 + 9e-7, FALSE],
       [1936.5, 3e-8, FALSE],
+
+      // These floats are valid, but might trigger FP math idiosyncrasies.
+      // @see https://www.drupal.org/node/2230909
+      [9990009888.96, 0.01, FALSE],
+      [9990009888.99, 0.01, TRUE],
+      [990088999.0096, 0.0001, TRUE],
+      [990088999.0099, 0.0001, FALSE],
     ];
   }
 
