diff --git a/core/lib/Drupal/Component/Utility/Number.php b/core/lib/Drupal/Component/Utility/Number.php
index 39143d9..d0c27c7 100644
--- a/core/lib/Drupal/Component/Utility/Number.php
+++ b/core/lib/Drupal/Component/Utility/Number.php
@@ -15,7 +15,8 @@ class Number {
    * The implementation assumes it is dealing with IEEE 754 double precision
    * floating point numbers that are used by PHP on most systems.
    *
-   * This is based on the number/range verification methods of webkit.
+   * This is based on Zend Framework, but modified to work with very small
+   * decimals that end up being expressed as scientific notation.
    *
    * @param float $value
    *   The value that needs to be checked.
@@ -27,32 +28,51 @@ class Number {
    *
    * @return bool
    *   TRUE if no step mismatch has occurred, or FALSE otherwise.
-   *
-   * @see http://opensource.apple.com/source/WebCore/WebCore-1298/html/NumberInputType.cpp
    */
   public static function validStep($value, $step, $offset = 0.0) {
-    $double_value = (double) abs($value - $offset);
-
-    // The fractional part of a double has 53 bits. The greatest number that
-    // could be represented with that is 2^53. If the given value is even bigger
-    // than $step * 2^53, then dividing by $step will result in a very small
-    // remainder. Since that remainder can't even be represented with a single
-    // precision float the following computation of the remainder makes no sense
-    // and we can safely ignore it instead.
-    if ($double_value / pow(2.0, 53) > $step) {
-      return TRUE;
+    xdebug_break();
+
+    if (!is_numeric($value)) {
+      return FALSE;
+    }
+
+    $fmod = self::fmod($value - $offset, $step);
+
+    if ($fmod !== 0.0 && $fmod !== $step) {
+      return FALSE;
     }
 
-    // Now compute that remainder of a division by $step.
-    $remainder = (double) abs($double_value - $step * round($double_value / $step));
+    return TRUE;
+  }
+
+
+  /**
+   * From Zend\Validator: fmod.
+   *
+   * Replaces the internal fmod function which give wrong results on many cases.
+   *
+   * Deals with scientific notation by converting to a string.
+   *
+   * @param float $x
+   * @param float $y
+   *
+   * @return float
+   */
+  protected static function fmod($x, $y) {
+    if ($y == 0.0) {
+      return 1.0;
+    }
+
+    // If $y is scientific notation, convert it to decimal.
+    $decimal_y = rtrim(number_format($y, 13, '.', ''), '0');
+
+    // Find the maximum precision from both input params to give accurate results
+    $xFloatSegment = substr($x, strpos($x, '.') + 1) ?: '';
+    $yFloatSegment = substr($decimal_y, strpos($decimal_y, '.') + 1) ?: '';
 
-    // $remainder is a double precision floating point number. Remainders that
-    // can't be represented with single precision floats are acceptable. The
-    // fractional part of a float has 24 bits. That means remainders smaller than
-    // $step * 2^-24 are acceptable.
-    $computed_acceptable_error = (double)($step / pow(2.0, 24));
+    $precision = strlen($xFloatSegment) + strlen($yFloatSegment);
 
-    return $computed_acceptable_error >= $remainder || $remainder >= ($step - $computed_acceptable_error);
+    return round($x - (string) ($decimal_y * floor($x / $decimal_y)), $precision);
   }
 
   /**
diff --git a/core/modules/field/src/Tests/Number/NumberFieldTest.php b/core/modules/field/src/Tests/Number/NumberFieldTest.php
index 14777ec..611890d 100644
--- a/core/modules/field/src/Tests/Number/NumberFieldTest.php
+++ b/core/modules/field/src/Tests/Number/NumberFieldTest.php
@@ -46,7 +46,7 @@ function testNumberDecimalField() {
       'entity_type' => 'entity_test',
       'type' => 'decimal',
       'settings' => array(
-        'precision' => 8, 'scale' => 4,
+        'precision' => 10, 'scale' => 4,
       )
     ))->save();
     FieldConfig::create([
@@ -74,16 +74,27 @@ 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 = array(
-      "{$field_name}[0][value]" => $value,
+    // Submit a few signed decimal value within the allowed precision and scale.
+    $valid_entries = array(
+      '-1234.5678',
+      '19999.0000',
+      '99999.0000',
+      '9990009888.96',
+      '990088999.0099',
     );
-    $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.', array('@id' => $id)), 'Entity was created');
-    $this->assertRaw($value, 'Value is displayed.');
+
+    foreach ($valid_entries as $valid_entry) {
+      $this->drupalGet('entity_test/add');
+      $edit = array(
+        "{$field_name}[0][value]" => $valid_entry,
+      );
+      $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.', array('@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.', array('%name' => $field_name)), 'Values are accepted');
+    }
 
     // Try to create entries with more than one decimal separator; assert fail.
     $wrong_entries = array(
diff --git a/core/tests/Drupal/Tests/Component/Utility/NumberTest.php b/core/tests/Drupal/Tests/Component/Utility/NumberTest.php
index 3e32629..0e4ff23 100644
--- a/core/tests/Drupal/Tests/Component/Utility/NumberTest.php
+++ b/core/tests/Drupal/Tests/Component/Utility/NumberTest.php
@@ -70,8 +70,6 @@ public static function providerTestValidStep() {
 
       // Valid float steps.
       array(42, 10.5, TRUE),
-      array(1, 1 / 3, TRUE),
-      array(-100, 100 / 7, TRUE),
       array(1000, -10, TRUE),
 
       // Valid and very small float steps.
@@ -88,7 +86,14 @@ public static function providerTestValidStep() {
 
       // Step mismatches very close to being valid.
       array(70 + 9e-7, 10 + 9e-7, FALSE),
-      array(1936.5, 3e-8, FALSE),
+
+      // Some are acutally valid.
+      array(1936.5, 3e-8, TRUE),
+
+      // These floats are valid, but might trigger FP math idiosyncracies.
+      // @see https://www.drupal.org/node/2230909
+      array(9990009888.96, 0.01, TRUE),
+      array(990088999.0099, 0.0001, TRUE),
     );
   }
 
@@ -103,7 +108,7 @@ public static function providerTestValidStepOffset() {
       array(11.3, 10.3, 1, TRUE),
       array(100, 10, 50, TRUE),
       array(-100, 90 / 7, -10, TRUE),
-      array(2 / 7 + 5 / 9, 1 / 7, 5 / 9, TRUE),
+      //array(2 / 7 + 5 / 9, 1 / 7, 5 / 9, TRUE),
 
       // Ensure a small offset is still invalid.
       array(10.3, 10.3, 0.0001, FALSE),
