Problem/Motivation
- Create decimal field, set precision to 10 (minimum in the UI and scale to 4
- Saving new node with value 19999.0000 succeeds (precision is 5+4 = 9).
- Saving new node with value 99999.0000 fails (same precision as above).
or
- Create decimal field, set scale to anything over 6 (need 8 to store bitcoin values for example). I used 16 as precission.
- Saving new node with value 20.12345678 fails validation while 0.1234567 succeeds.
This is because Drupal\Component\Utility\Number::validStep() returns false. Detailed investigation on this function (which is only used once in Drupal), reveals that the first argument ($value) is received as a string, but $step and $offset are floats. PHP seems to slightly mangle the $step value on the first case above from 0.0001 to 0.00010000000000000001438. Passing the step parameter as a string in the case of decimal numbers maintains the correct precision, and allows a correct approximation calculation.
Proposed resolution
Bypass weak PHP floating-point handling by passing the step as a string.
Remaining tasks
Merge. Commit. Decimals FTW!
User interface changes
None.
API changes
None.
Data model changes
None.
Possible workaround if you need big decimals
Disable this validation by setting the render element #step
to 'any'
i.e.
$element['#step'] = 'any';
In the case of fields, you can do
function MYMODULE_form_FORM_WITH_BIG_DECIMAL_FIELD_alter (array &$form, FormStateInterface $form_state) {
$form['field_some']['widget'][0]['value']['#step']='any';
}
And if you want to target all decimal fields:
/**
* Prevents validation of decimal numbers
* @see https://www.drupal.org/node/2230909
*/
function MYMODULE_field_widget_form_alter(&$element, \Drupal\Core\Form\FormStateInterface $form_state, $context) {
$field_definition = $context['items']->getFieldDefinition();
if ($field_definition->getType() == 'decimal') {
$element['value']['#step'] = 'any';
}
}