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';
}
}
| Comment | File | Size | Author |
|---|
Issue fork drupal-2230909
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
- 2230909-simple-decimals-fail
changes, plain diff MR !8282 /
changes, plain diff MR !579
- 2230909-11.3.x-hacky-disable-validation
changes, plain diff MR !15288
- 11.3.x
compare
- 2230909-hacky-disable-validateNumber
compare
- 9.2.x
compare
- 2230909-10.6.x
changes, plain diff MR !14204
- 2230909-testing-stable
changes, plain diff MR !14283
- 2230909-11.3.x
changes, plain diff MR !14280
Comments
Comment #1
djevans commentedI've added the following test data to NumberTest::providerTestValidStep() on my local machine:
and the test still passes. Similarly, executing
at /devel/php prints 'Yes'.
It looks like Number::validStep() is producing a non-zero remainder when it's called via form_validate_number() as opposed to being called directly, but I'm not sure why this is just yet.
Comment #2
Anonymous (not verified) commentedI can confirm djevans conclusion that the problem lies in the calculation of the remainder.
For the unit test the remainder simply is 0, but when debugging the actual flow I get -1.4210854715202E-14.
The exact values I found (evaluated as watches) were:
As far as I can tell, this issue is caused by the floating point format that makes rounding errors. But I'm really no expert in this.
Comment #3
JensH commentedI also can confirm this issue. Having a decimal field with precision: 14 and scale: 2, entering the value "3333333" is vaild, while "4444444" will fail Number::validStep vaildation in form_validate_number() in form.inc.
Comment #4
exlin commentedIssue still seems to exist. What is your opinion, should this issues priority be boosted to major?
Comment #5
exlin commentedReturns:
And since remainder is about 9E-15 higher than calculated acceptable error form validation gives the error. Also as validateNumber() seems to get exactly right values the good question is at which point there becomes some "additional invisible decimals" to given value and why $round & $double_value seems to be the same.
Comment #6
exlin commentedPassing arguments as a string fixes issue for me. It's not particularly beautiful since we don't know exactly where things goes wrong but it's better than non-working decimal field.
Comment #7
exlin commentedUpdated status
Comment #9
dawehnerComment #10
jcnventuraRaising this to major, as decimals are fundamentally broken at this time. Even simple basic precision/scale values seem to fail for reasonable values.
No interdiff provided, as the patch in #6 was basically a terrible hack (as acknowledged by @exlin when he submitted it), and wasn't used in the fix.
Comment #14
jcnventuraMy bad.. The approach of not calling validStep for non-floats is wrong. Integers can have steps, so we should call validStep to verify them.
HOWEVER, it seems weird to use a function that is clearly aimed at checking floating-point step (and approximation errors), only for non-floating point numbers.. This because validStep is usually not called for float fields since these have step = any (integers have step=1, and decimals have step = 1^-scale).
So the newly added tests are correct, but I'm really having doubts on the usefulness of the current implementation of validStep. It should probably simply be checking if the value-min is a multiple of step.
Comment #15
jcnventuraComment #16
jcnventura@exlin was right.. The only way to do this correctly is to cast step to a string.
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.
Comment #18
extexan commentedI had the issue described here...
https://www.drupal.org/node/2693467
...which was marked as a duplicate of this one. I applied patch 16 and it fixed my issue.
Comment #19
alexpottThe code here seems to suggest this documentation is wrong.
I'd also expect some changes to
Drupal\Tests\Component\Utility\NumberTestComment #20
alexpottComment #22
bc commentedHere's a failing test for Number::validStep added into the testonly patch from above.
The fix supplied above doesn't help with validStep not working.
Comment #23
jcnventuraLet's use #22 as the testonly patch.
Comment #24
jcnventuraThis patch hopefully addresses the documentation request from @alexpott on #19.
It includes @bc's expanded test from #22, and some minor coding style changes.
Comment #27
jcnventuraSomething deep is going on here, and we may have to redo this validStep function from scratch.. But for now, let me just save my current work. Some failing tests are commented out, and others are actually configured to report FALSE when they should be returning TRUE.
Comment #29
jcnventuraWhat are you doing testbot?
Comment #31
jcnventuraComment #33
bc commentedjcventura, the tests that i added should be failing. Once the bug in Number::validStep is fixed, the tests will pass.
I think we should just parse the numbers as strings, rather than dealing with weird float issues. I can rewrite it if there are no objections.
Comment #34
bc commentedHere's a somewhat complete fix for validStep -- i've adapted code from zend framework's Zend\Validator\Step, but tweaked it to work with very small floats.
I removed a couple tests that don't pass -- if anyone knows how to make them pass, please modify :)
I'll work on it more tonight to try & get them to pass.
Comment #35
bc commentedComment #37
bc commentedoops, the last patch i created was total trash; here is one that only has changes to validStep & the tests.
i added back the original (8.2.x) potentially useless tests that check for things like 100/7 -- will drupal ever be saving the floating point result of integer division into a field value?
Comment #39
NetNerdy commentedtested patch #27 in drupal 8.1.8...fail
can't save/create a field decimal value like 9'999'999.99 or above.
is there any other solution?
i need values up to e.g. $ 999'999'999.99
thanks in advance
Comment #40
NetNerdy commentedtested patch #27 in drupal 8.1.8...fail
can't save/create a field decimal value like 9'999'999.99 or above.
is there any other solution?
i need values up to e.g. $ 999'999'999.99
thanks in advance
Comment #41
bc commented@NerdyCrowd have you tried my patch from #37? if it fails can you add the tests that should pass to core/tests/Drupal/Tests/Component/Utility/NumberTest.php?
Comment #42
NetNerdy commentedtested patch #27 on fresh drupal 8.1.8 localhost php7= pass
@bc...i will test your patch #37 within next days
Comment #43
inversed commentedPatch #27 solved a problem I was having on Drupal 8.1.8. I was getting an error with a valid number when submitting a decimal somewhere greater than 3,560,000.00. So this issue doesn't just effect small numbers.
Are there any know down sides to the 8.1.8 patch? One thing in particular I'm seeing is that validation no longer prevents entering a value larger than the DB permits. For example, @NerdyCrowd - I can submit a value as great as 999,999,999.99. I've got my field set to a precision of 12 and a scale of 2. However, I can't submit a value of 12,999,999,999.99 as that gives me an error like this:
Drupal\Core\Entity\EntityStorageException: SQLSTATE[22003]: Numeric value out of range: 1264 Out of range value for columnComment #45
NetNerdy commented@inversed
patch #27 tested again in drupal 8.1.10...
I've got my field set to a precision of 16 and a scale of 2...and the biggest value is 90.000.000.000,00. i need values max. 9.999.999.999,99...so this patch does it for me.
Comment #46
thlor commentedPatch #27 fails for me with the following setup:
- Decimal field, precision: 10, scale: 7
- Trying to save this number fails: -3.1933172
The same number saves successfully without the patch.
Comment #47
pianomansam commentedHow is this coming along? I'm running 8.2.4 and tried #37 to fix entry of large numbers like 4,031,239,412.52 on a 20/2 field. While that worked, small numbers like 1109.87 became invalid. For the time being I'm running #16 as it appears to be working in both of these instances.
Comment #48
bc commented@pianomansam when I add those numbers to `core/tests/Drupal/Tests/Component/Utility/NumberTest.php` they pass with my patch -- i wonder what is causing the validation failure...
also i've just noticed that for whatever reason, when i apply my patch in #37 to 8.2.x latest, these fractional test cases fail:
Comment #49
bc commentedactually i think i know what is causing the validation failure -- the casting issue that jcventura's patch deals with.
maybe the best approach is to combine jcventura's patch in #16 and mine in #37.
Comment #50
jcnventura@bc Yes, as long as you're passing the value as a float/double to validStep, you've already lost a lot of precision.. Just pass it as a string like I did in #27, and then you can try to optimize the validStep function.
I know it's awful to pass it as a string, but once you look at the values you get at the start of validStep if you don't do that, you'll also concede defeat and simply pass it a string.. Believe me, I tried hard not to have to do it.
Comment #52
damondt commentedQuick work around in case people are stuck without a solution until this is resolved: https://gist.github.com/ummdorian/eaece93165df0bdd6546b07614e955e4
Comment #53
jwilson3Patch in #16 worked for me with the following scenarios, that were failing without this patch:
- Decimal field, precision: 10, scale: 2
- Trying to save this number: 70000000
- Trying to save this number: 70000000.00
Comment #54
pwolanin commentedHere's a straight re-roll of #27 for 8.4.x
The test fail is reproducible but somewhat bogus since it's complaining about a class mentioned in comment.
Comment #56
perecedero commentedQuick work around, do not validate #step
Comment #57
pwolanin commentedNot sure if this is a Chrome browser bug, but the with a high precision, using the up/down arrows in the field will sometimes give you a number in scientific notation like
3e-8.This fails validation even with this patch:
The attribute in the HTML is:
step="1.0E-8"full element:
<input data-drupal-selector="edit-field-chemical-amount-0-value" type="number" id="edit-field-chemical-amount-0-value" name="field_chemical_amount[0][value]" value="3e-8" step="1.0E-8" min="0" placeholder="" class="form-number error" aria-invalid="true" />Comment #58
andrewhdI've manually tested and confirmed the use cases in the issue summary and #3, #46, #47, #53 work using the patch in #54. The test fail due to a class mentioned in a comment still exists.
Comment #59
cilefen commentedI am updating credit for the triage work. Going forward, you should know that we like to see documented triage steps (even if brief). It is the only way to know if the triage has actually been completed. Here are some made-up examples of documented triage steps:
Thank you!
Comment #60
nor4a commentedI'm using #54 patch. For some numbers it does work, but for some not.
13517282.20 - this fails
13517282.21 - this passes
tell me why? :)
The storage is using the default decimal field settings e.g.
precision: 10
scale: 2
Comment #61
hanoiiI second @Nor4a that those values fails and not as he said. Very odd. I tried looking but the math on validStep is a bit over my head, at least currently :).
Comment #62
hanoiiAnd also second the workaround on #56. I ended up doing the same
Comment #63
hanoiiComment #64
hanoiiComment #66
xjmAdding a similar issue that @larowlan located.
Comment #67
larowlanThis was discussed on the major triage call by @cottser, @catch, @xjm, @effulgentsia, @cilifen and myself and agreed it was major on the following basis:
Comment #68
tacituseu commentedRe-rolling from #27 as #54 ate the
$wrong_entries = []part of the test, picked a comment from #54 and fix of misclassified test from #37.This patch will fail.
Comment #69
tacituseu commented@Nor4a:
To see why that is try setting
ini_set('precision', 32);, then it turns out:0.01 is actually 0.010000000000000000`208166817117217
13517282.20 is 13517282.199999999`254941940307617
13517282.21 is 13517282.210000000`894069671630859
after the 17th significant digit (marked with `) illusion falls apart (not enough bits in double).
The culprit is mentioned in #2, inside Number::validStep() there is:
$remainder = (double) abs($double_value - $step * round($double_value / $step));This is the trouble maker, especially
$step * round($double_value / $step)because by dividing
$double_valueby$stepyou effectively increase its order by the precision of the $step so it becomes 1351728220,which when multiplied by $step is enough orders of magnitude to bring the rounding errors into play,
and after subtraction it makes enough of a difference (0.00000000186264514923095703125) to go over
$computed_acceptable_error(0.00000000059604644775390626240770918829542)13517282.21 passes because appoximate 13517282.21 is more closely divided by approximate 0.01 than approximate 13517282.20 is.
The code all of this is based on moved to https://github.com/WebKit/webkit/blob/master/Source/WebCore/html/StepRange.cpp but hasn't improved much.
By not reaching into the abyss deeper than is necessary it should be possible to mitigate most of the problems, but you can only do so much with doubles, that's why Zend based fmod patch in #37 has its own problems. Added more test cases mentioned in the issue.
This patch should pass.
Edit: Also the comments on the original make me suspect the author might have been a bit confused about the difference between fixed-point and floating-point representations of the floats.
Comment #72
tacituseu commentedFixing comment,
Drupal\Componentshould know nothing aboutDrupal\Core, even comments.Comment #73
tacituseu commentedTest only, should fail.
Comment #75
tacituseu commentedNote:
Number::validStep()states:The step scale factor. Must be positive.but it is tested in NumberTest::providerTestValidStep() with
[1000, -10, TRUE],Comment #76
bc commented@tacituseu thank you for your effort in consolidating all of the previous discussion & coming up with a really great fix for this issue! even though the project i'd originally been working on this bug has come & gone, it still haunts me from time to time. in addition to being a comprehensive solution for the bug, you explained your thought process, and educated all of us. i really appreciate your work here.
Comment #77
operinko commentedI'm taking a look at this.
Comment #78
operinko commentedThe patch from #72 does seem to fix the issue, but the patch was no longer applying cleanly to 8.5.x branch. Rerolled with fix for the non-applying chunk.
Comment #79
operinko commentedSince I only rerolled the patch to get it applying cleanly, manual testing showed it fixing the issues at hand and the automatic tests are passing:
RTBC'd
Comment #80
larowlanStill needs review
Comment #81
borisson_I wonder why we're not doing this with a dataprovider.
--
Oh, because of browser tests, okay - nevermind.
I still think this looks weird but in the interest of testspeed I understand why this is happening.
Can we rewrite this comment? I understand this sentence up until "valid".
How about something like this? Not sure if this is even correct.
Comment #83
Pascal- commentedTested on Drupal 8.5.0 and works.
Comment #84
Pascal- commentedOops ... Didn't notice this was back to needs work, should probably leave it like that as explained in #81
Comment #85
xanoThis updates the comment @borisson_ pointed out and *should* make it clearer.
Since the precision computation code is one of the most complex pieces, I split if off to its own method. This allows for more dedicated documentation and testing, as well as reusability of this generic code which is easy to get wrong.
Comment #86
borisson_This does make it a lot easier to read, good work @Xano! I really like the additional testcoverage.
I feel really bad for doing this but:
bracket position is incorrect.
Comment #87
tacituseu commentedAlso comment from the part mentioned in #81.1
they're no longer only signed.
Comment #88
xanoThanks for the reviews! :)
Comment #89
borisson_I'm pretty sure that patch contains too many things @Xano. It went from 10kb to 91kb. I guess you were also working on something else? The interdiff looks great though.
Comment #90
xanoAh, meh :D I make interdiffs on the same branch, but patches between branches. I guess I diffed against the wrong branch. New patch coming soon!
Comment #91
xanoIt helps if you rebase before generating diffs, Xano...
Comment #92
borisson_All the nitpicks that were posted since #80 have been resolved, and this was RTBC before.
Comment #93
alexpottThese docs contradict themselves. "The implementation assumes it is dealing with IEEE 754 double precision" and "which are not stored in IEEE 754 format".
This needs to explain why 13 is chosen here.
This documentation is not easy to read. I suggest changing it to something like:
The step is not valid if the value has a higher precision than the desired precision. This is because the value will not be divisible by the step.What is $expected? Also the documentation below the line seems to belong above.
As far as I can see this could is not tested.
Perhaps rather than trying to merge all the fix into \Drupal\Component\Utility\Number::validStep we could introduce a new method for decimals aka non IEEE 754 numbers.
Comment #94
xanoI brushed off my math (read: Wikipedia) skills and dug into this a little more.
This looks intended to chop off any insignificant digits, but I don't know where 13 comes from. IEEE 754 double precision floats have 53-bit significands, leading to
floor(53 * log(2)) = 15guaranteed significant decimal digits (Kahan, 1997). I briefly tested this manually against a float with 25 decimals and after 15 decimals rounding errors started to show up after conversions. This leads me to believe 13 must instead be 15.However, PHP does not guarantee it uses IEEE 754 for floats:
(emphasis mine, source)
Comment #95
tacituseu commentedRe #93:
2. This is from #34 but is not part of
Zend\Validator\Step, it seems to be a 'test passing constant' ;) asDrupal\Tests\Component\Utility\NumberTest::providerTestValidStep()has this:[3.9999999999999, 1e-13, TRUE],4. It is the closest evenly divisible by
$stepnumber, or a number it would expect to see if it was a valid stepComment #96
xano#34 seems to indicate the
13comes fromzendframework/zend-validator, but it's never been a part of\Zend\Validator\Step.Also, what's preventing us from adding this Zend package as a dependency?
Comment #97
tacituseu commented@Xano: it has its own/similar problems, so it won't pass.
Comment #98
alemadleiI used the patch at #85.
However, saving data (latitude, longitude) is not working for me.
I created a content entity using Drupal Console, I modified and added the following.
During data submission, the form structure I get is:
So, when validStep is executed, the precision I always get is 6.
1.0E-12 represents the precision I want, 12, however
Always returns 6, causing an error stating the the number is not valid.
Any ideas on how can I enforce the full representation to be used so that it properly calculates the precision?
Comment #99
alemadleiSo, I discovered that if I do this change manually during the buildForm() I can get it to work, even without the patch.
Comment #100
tacituseu commented@alemadlei: 6 is because
static::getPrecision()fails to do the expansion if the step is a stringand it ends up this way in the form because of:
and
Also, 18 digits of precision is too much to ask of a
double.Comment #101
xanoThat is becauseyour 1.0E-12 is actually a string, and not a numeric value, and currently the only string formats supported are integer and float values. I don't think adding support for all those cases is a good idea. Instead, this API should take a few simple, predictable input types, and user input parsing should be done elsewhere. However, this API cannot be cleaned up without breaking BC, so I suspect the core committers will see adding this support as the only realistic way forward, but it won't be particularly clean.
Comment #102
xanoThis should clear up a few things :)
Comment #103
anpolimusExpecting the same issue at my project.
I have Decimal field with 12 digits and scale=2.
Patch at the #102 fixes this issue.
Thanks @Xano for this fix.
Code at the patch looks good for me.
Moving to Needs Review
Comment #104
runeasgar commented$ curl -l https://www.drupal.org/files/issues/2018-04-18/drupal_2230909_102.patch | git apply -vdrush crIf the second use case in the description is no longer valid, then this is RTBC. Otherwise, it needs work. I'm not sure, so leaving it as "Needs review".
Comment #105
anpolimusI've found that existed code doesent check properly out of range numbers.
I have Decimal field with 12 digits and 2 for digits after dot.
I'm trying to enter 10 000 000 000 and getting mysql error: Numeric value out of range:
It means that high border validation doesent checked.
Comment #106
tacituseu commented@anpolimus: That would be #1003692: PDOException: SQLSTATE[22003] after entering large value in integer and decimal field.
Comment #107
tacituseu commentedRe #101: The thing with
1.0E-12is it looks like it is the patch itself that introduces the problem (see #100), and then tries to undo it withnumber_format(), maybe replacing the casting withnumber_format()would make it cleanerRe #102:
why change in casting ? they might be somewhat synonymous in PHP but it still feels wrong.
Re #104: about the second case, it does feel wrong to fail like that, either:
1. there should be enforced upper limit on precision (15)
2. do as #93.6 proposed and use some arbitrary precision math library for that
Comment #108
xanoThey are identical, but "float(ing point number)" is the official name, so the change was made for consistency's and clarity's sake.
Comment #109
xanoThis patch introduces a new method strictly for normalizing different numeric values into the simple string format we use in core. It's also a format used by BCMath, so I documented that as well. Furthermore, it addresses lack of test coverage reported in #93 by reverting the change, because the code normalizes all values under the hood now. Additionally, I fixed some documentation.
After discussion with @alexpott on IRC, we concluded that strings using scientific notation will not be supported. Scientific notation is for use in code only (after all, it is syntax sugar for ordinary floats), and string-based numeric values, such as from form input, will have to remain in an integer or plain float (digits with a decimal period) format.
Comment #110
tacituseu commented1.
If there's to be a limit it should be on
$element['precision']as floating point representation guarantees a specific number of significant decimal digits, and not amount of 'digits to the right of the decimal'.2.
This method name might be a bit confusing with
FieldTypedefining 'precision' as:'#description' => t('The total number of digits to store in the database, including those to the right of the decimal.'),Comment #111
xanoThanks! What about this?
Comment #112
xanoThis fixes a typo introduced by the previous patch, and improves some additional variable names, as well as documentation.
Comment #113
xanoThis patch adds test coverage to ensure the minimum number of significant digits is indeed enforced as an upper bound when counting a numbers significant decimals, as well as coverage to ensure this bound is not enforced for float-formatted strings, as well as documentation why.
Comment #114
tacituseu commented1. Can't help much with naming, as a non-native speaker I've no idea what is the common way to refer to it, the least ambiguous to me seems to be 'decimal places'.
Looked around a bit and at least MySQL, SQL Server and BCMath seem to refer to it as scale though.
2,
+ public function testGetPrecision($expected, $number) {+ public function provideGetPrecision() {nitpick, but it still references old function name, also 'provider'.
Edit:
3.
Didn't notice this earlier, but casting to string results in scientific notation in some cases (as shown in #100), which will make it not work as intended, will try to add a test case for that later.
Comment #115
pwolanin commentedI'm seeing a problem with the value populated into the HTML5 validation attribute which stops a form from submitting. This is using Drupal 8.5.2 and PHP 7.2.
The markup is:
Note the trailing 5 in the step attribute.
The base field definition is:
Comment #116
pwolanin commentedA quick and dirty fix in \Drupal\Core\Field\Plugin\Field\FieldWidget\NumberWidget::formElement looks roughly like this where the 1st changed line is already part of this patch.
I'm really not sure where in the render flow it's getting mangled, but this is roughly the same solution (handle as string)
Comment #117
pwolanin commentedHere's a patch with a fix included as above (but using number_format()) plus in \Drupal\Core\Field\Plugin\Field\FieldType\DecimalItem::fieldSettingsForm
Does there need to be added validation for base fields that the scale is not more than Number::IEEE_754_DOUBLE_GUARANTEED_SIGNIFICANT_DECIMALS ?
Comment #118
xano@pwolanin Thanks!
@tacituseu Good catch! The attached patch should take care of this. It also converts a few code comments from
#to//, because I realized this is not Python... ;-)Comment #119
xanoThis adds some documentation, and adds the field-level validation @pwolanin brought up in #117.
Comment #120
tacituseu commentedAdded test case for #114.3 and also one for the other way it could go wrong.
Comment #122
tacituseu commentedSomehow for #119 only functional tests ran (results), so either I rolled the patch wrong or it introduced new failures, retesting.
Comment #123
xano@tacituseu: #118 contains a fix (the
preg_match()call) for the bug you tried to expose in #122. It basically checks if the string cast results in a number of decimals within the minimum guaranteed range of significant decimals (1-15). If not (because too much precision or scientific notation), it uses the existingnumber_format()solution). This handles PHP's unreliable float -> string cast, but also floats that are cast reliably, but have less than 15 significant decimals, and applying ournumber_format()approach to such numbers would result in the numbers being padded to the right until there are 15 decimal digits, which will automatically result in a false positive precision of 15 digits (due to the padding, even though the original float had perhaps only 3 significant decimal digits).Comment #124
tacituseu commented@Xano: looks like this part:
is responsible for the failures.
Comment #125
xanoThis fixes the test failure by removing the field value constraint I added for unknown reasons (constraints cannot be applied to field settings), and it applies the jargon changes to the tests (thanks to @tacituseu for pointing that out).
Comment #127
lapek commentedThe patch in #125 (drupal_2230909_124.patch) fails to apply on Drupal 8.6.0
Comment #129
lapek commentedReroll for 8.6 and 8.7
Comment #130
lapek commentedI jacked up my previous patch
Comment #131
lapek commentedAttempting this again since this prevents us from editing nodes migrated from D7
Comment #132
lapek commentedComment #133
psf_ commentedI applied this path and it's ok, for me. I review the test and I think that it's ok.
Comment #134
exlin commentedGreat to hear that we are making progress, this has been originally reported 4,5 years ago so it has lived long ;)
Comment #135
alexpottsignificant decimalsand whether it should besignificant decimal digitsbut I was wrong -significant decimalsis used on the web so no change necessary for that.I think it is important to mention that floats with a higher number of significant decimals than
IEEE_754_DOUBLE_GUARANTEED_SIGNIFICANT_DECIMALSwill lose the additional precision as it is not guaranteed by PHP.This reads a bit odd. How about
The normalised numeric string.? It can contain a minus sign as well for example.There is no explicit testing of this method. As we're making it public there should be.
It feels important here to mention that floats are limited to the precision guaranteed by PHP - ie. 15. Hence you get things like
Comment #137
apolitsin commentedComment #138
baluertlToday tested, @lapek's patch from #131 applies cleanly on 8.8.x HEAD.
To address @alexpott's observations:
Comment #139
apolitsin commentedWill it ever be fixed?
This bug prevents us from every second project for the sale of real estate, when we try to save prices in rubles
Comment #140
baluertl@APolitsin could you please provide a unit test to address this last remaining point from my previous list?
AFAICT this seems the last remaining todo from @alexpott's comment #135. So if someone writes a unit test, then it can be reviewed and set to RTBC again.
Comment #141
Greenskin85 commentedThis isn't fixed in core while it exists for 5 years because of a missing test?
Comment #142
krzysztof domański1. I think we should add a separate issue to work on the method to count significant decimals. New method
Number::countSignificantDecimals()does not affect the current HEAD so it can be reviewed and commited faster.2. I created #3082919: Add to \Drupal\Component\Utility\Number a method to count significant decimals. It has a new test and changes according to #135 that needs review.
Comment #143
temkin commentedJust confirming that #138 fixes the issue for us.
Comment #144
krzysztof domańskiPostponed due to #142. This issue is not trivial and requires more attention during the review. IMO we should finish #3082919: Add to \Drupal\Component\Utility\Number a method to count significant decimals first.
Comment #146
ivnishThank you! The patch save my time
Comment #147
drclaw commentedSeems like this was close to being accepted. Not sure if splitting off
Number countSignificantDecimals()is the best idea considering? Maybe it is, but in the case that it isn't, here is the patch from #138 with the Unit test from #3082919: Add to \Drupal\Component\Utility\Number a method to count significant decimals.Comment #148
jungleMissing parameter name
- @param int|float|string
+ @param $number int|float|string
Comment #150
thhafner commented#138 and #147 patches are failing to apply for 8.9.x and 9.1.x.
Comment #151
hardik_patel_12 commentedRe-roll for 9.1.x-dev.
Comment #152
hardik_patel_12 commentedComment #153
thhafner commented#151 works well for me on 8.8.x, 8.9.x, and 9.1.x
Comment #154
maxilein commented#151 drupal_2230909_151.patch works well for me on 8.8.5 (php 7.4) where I applied the patch without problems and large numbers seem to work at first glance.
BUT:
I have a 20/2 decimal field - not a default 10/2:
When I enter 123456789123456789.12 it is saving without problems but I get: 123456789123460000.00
When I enter 12345678912346.01 I get the old error "is not a valid number."
Same for 1234567891234.01
When I enter 123456789123.01 it gets saved correctly. No error. And so do apparently do all shorter numbers.
So there is still some inconsistency with the patch here.
Comment #155
maxilein commentedComment #156
maxilein commentedMaybe there should be a warning in the interface that remarks the platform's max. of precision on the system (PHP_FLOAT_MAX).
It does not make sense to allow field settings for a larger number than technically possible by the underlying system.
Comment #157
pavnish commentedComment #158
pavnish commented@maxilein can you please check this patch the #154 has been resolved
This is due to the presave in decimal DecimalItem.php and and numberFormat function in core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/DecimalFormatter.php
I don't know why we again change this value in numberFormat while we are saving the correct value in DB.
Comment #160
pavnish commentedComment #161
pavnish commentedWorking on #156
@maxilein this is due to the number_format function.
The bast solution is to add validation in field settings
Comment #162
pavnish commented@maxilein I have changed the #max value 32 to 16 it's working fine.
The max value supported is 99999999999999.99 which i have checked .
It's meet the #156
Comment #163
maxilein commentedHi,
thank you. For testing I created a new field "testdecimal". Selection of a maximum 16 precision upon creation is ok.
So I chose 16 with scale 2.
But when I enter your maximum:
99999999999999.99 ->1 error has been found: testdecimal is not a valid number.
9999999999999.99 ->1 error has been found: testdecimal is not a valid number.
999999999999.99 -> saves ok. (Thats 14 digits 16-2?)
The behaviour is consistent for the new and also already existing fields.
Maybe the max precision value is misleading? Does it include the scale?
Comment #164
pavnish commented@maxilein yes it's including the scale
Comment #165
maxilein commentedThen it is wrong. It is missing 2 digits.
You can see that the working example above in #163 has only 14 digits (12+2), when it should have 16(14+2) like the number you tested.
Comment #166
pavnish commented@maxilein it was working fine for me .but no problem I will test again and will attach a video.
Comment #167
pavnish commentedComment #168
maxilein commentedDid your latest patch, patch your patch from #151?
I just used 8.8.6 and applied #162. Maybe thats what I do wrong?
Comment #169
pavnish commented@maxilein i used drupal 9.1 and #162
Comment #170
maxilein commentedOk. I have 8.8.6 on php 7.4 ubuntu 20.04
Comment #171
maxilein commentedSo then #163 is still a problem. I can only save 14 digits (12+2) - when according to your test it should be 16 (14+2).
It applies to new and existing fields after applying #163
Comment #172
maxilein commentedComment #173
pavnish commentedHi @maxilein yes you are correct .
This is working fine with 14 digits (12+2) so i have changed the patch accordingly .
Comment #174
maxilein commentedLooks good to me!
Comment #175
samiullah commentedWhile testing the new patch when i changed the scale of the decimal field, I got the following exception:
Attempt to update field dec failed: Exception thrown while performing a schema update. SQLSTATE[42000]: Syntax error or access violation: 1427 For float(M,D), double(M,D) or decimal(M,D), M must be >= D (column 'field_dec_value').: CREATE TABLE {node__field_dec} ( `bundle` VARCHAR(128) CHARACTER SET ascii COLLATE ascii_general_ci NOT NULL DEFAULT '' COMMENT 'The field instance bundle to which this row belongs, used when deleting a field instance', `deleted` TINYINT NOT NULL DEFAULT 0 COMMENT 'A boolean indicating whether this data item has been deleted', `entity_id` INT unsigned NOT NULL COMMENT 'The entity id this data is attached to', `revision_id` INT unsigned NOT NULL COMMENT 'The entity revision id this data is attached to', `langcode` VARCHAR(32) CHARACTER SET ascii COLLATE ascii_general_ci NOT NULL DEFAULT '' COMMENT 'The language code for this data item.', `delta` INT unsigned NOT NULL COMMENT 'The sequence number for this data item, used for multi-value fields', `field_dec_value` DECIMAL(14, 15) NOT NULL, PRIMARY KEY (`entity_id`, `deleted`, `delta`, `langcode`), INDEX `bundle` (`bundle`), INDEX `revision_id` (`revision_id`) ) ENGINE = InnoDB DEFAULT CHARACTER SET utf8mb4 COMMENT 'Data storage for node field field_dec.'; Array ( ) .Comment #176
pavnish commented@samiullah Thanks for the checking.
This is due to the max value of scale is 15 and according to the patch the max value of Precision is 14.
The value of scale must be under Precision value.
Thanks
Pavnish
Comment #177
maxilein commentedWorks for me.
Comment #178
holist commentedLooks to me like everything else has been addressed but the comment clarification in #135.2.
Comment #179
holist commentedI changed the comment as proposed in #135, I agree there is no need to further qualify the normalization but it rather can make the docs confusing. Also slight rerolling was needed.
Comment #181
holist commentedAdded a code style fix, parameter documentation was wrong. I don't really understand what went wrong with the last test run though...
Comment #183
anmolgoyal74 commentedFixed the deprecated assertions.
Comment #184
kris_nikolov commentedTests pass and patch looks ok. RTBC!
Comment #185
maxilein commentedThe patch #183 does not work with D 8.9.6
Failed for NumberFieldTest.php:
Comment #186
holist commentedHere's #173 with just the comment change from #179 without rerolling needed for 9.1.x, should apply to 8.9.6, @maxilein.
Comment #187
alexpottAre we sure about 15? For the PHP documentation we have
- see https://www.php.net/manual/en/language.types.float.php
spelling - string.
This could be simplified to return
(string) $number;we can lose the elseif and the is_int() because the cast is going to check the type anyway - less logic and doing less.Why the default value change here? The code comments later:
BUT If I change this back to default to 0.0 and run \Drupal\Tests\Component\Utility\NumberTest it passes so either we're missing test coverage or this change is unnecessary.
We're setting $offset to 0.0 here anyway so I'm not sure I understand why the default value change is important.
Comment #188
ayushmishra206 commentedComment #189
ayushmishra206 commentedMade changes #187.1 #187.2 #187.3 in this patch. Please review.
Comment #190
ayushmishra206 commentedBack to needs work for remaining changes.
Comment #191
alexpottfor what it is worth I don't think this number should be higher than the PHP variable
precision- see http://php.net/precision - the default value is 14 but there's a question about why we are defining a constant here at all. And also what happens when a user set's the PHP variable to something else.Comment #192
paulocsHello,
I created a new patch for the 9.1.x-dev branch.
I did the changes suggested on comment 187.
I also removed the constant variable as @alexpott pointed on comment #191.
Cheers, Paulo.
Comment #193
holist commentedThanks @paulocs for the update. I however think what @alexpott meant was that we should not hardcode a value for precision as it can be different from configuration to configuration. So not defining a constant but still using the same value is not really the solution we should be looking at here. Instead we could just get the
precisionvalue from the PHP runtime configuration withini_get()so whatever is set up in the environment will be used.Comment #194
krug commentedDrupal Version: 8.9.5
nginx/1.14.2
PHP Version: 7.3
Database Version: 5.5.5-10.3.23-MariaDB
Filed Number (decimal)
Precision: 10
Scale: 2
For a value above 3999999,99 "field is not a valid number".
Тemporarily solved based on #56
In the admin theme for editing and creating content, in the part after
function ADMIN_THEME_form_alter(array &$form, FormStateInterface $form_state, $form_id) {
...
ADD
$form['field_your_field']['widget'][0]['value']['#step'] = 'any';
Comment #196
sokru commentedThis is reroll from #192, with issue pointed out on #191 and #193.
Comment #197
holist commentedThanks @sokru for the update! Tests pass, I'd say this is (again) at a point where all raised concerns have been addressed, so RTBCing.
Comment #198
alexpottShould this be converted to ini_get('precision')? Also do we need to ensure it's bigger than #min?
This constant does not exist.
Comment #199
sokru commentedAddressing the issues mentioned on #198.
Comment #200
alexpottI'm not sure this work - https://3v4l.org/AJSO8. I think we have to handle this after creating the array.
The issue this highlights is that we need an update path :( - some site's will have fields configured with impossible precisions. We need to work out how to update that field config. Even more tricky is what if the updates impact the data?!?!?
All this changes are out-of-scope. There are other issues focussing on replace assertRaw().
The message 'Entity was created' is not necessary - as we have to change this line it is in scope. Also this should use
$this->assertSession()->pageTextContains()and should not use t(). It could assert on the text"entity_test $id has been created."pageTextContains() - I don't know why this has to be in the raw html.
This is not necessary. If the entity didn't validate we would not have the created message.
Comment #201
alexpottAlso #200.1 implies that we don't have actual test coverage of this form! If I try out #199 locally I get PHP warnings.
Comment #202
andypostplease replace 3 function calls with local variable
Comment #203
maxilein commented#199 addition to the patch for the NumberFieldTest.php which did not patch on D8.9.8
Comment #204
laravz commentedHi,
We have applied patch #186 (the last working applicable patch for D8) to our working project and noticed that the site gets a fatal error when using a very large number.
decimal number: 9831510254545454000,00
field settings: decimals 3, precision 10
Drupal\Core\Entity\EntityStorageException: SQLSTATE[22003]: Numeric value out of range: 1264 Out of range value for columnWe have seen the same error with previous patches. Is this something that should be fixed here?
Comment #205
maxilein commentedUsing the patch #199 with #203 (which is of no importance I think) on D.8.9.8: Creating a new decimal field give Error on step 2 "field settings":
The field does get created and seems to work though.
Comment #206
maxilein commentedHere is the combination of #199 and #203 in one file.
Patches on 8.9.11
Comment #207
o'briatHere's something strange, maybe it's only related to my dev env, but when testing this validation from within xdebug or drush php /ev, it return TRUE :
My field is 20 + 2
php 7.4.12 (docker4drupal stack)
Comment #208
rafaelaleung commentedRerolling patch from #206 for 9.2.x. Have not created interdiff since I was not able to apply the original patch on 9.2.x.
Comment #209
rafaelaleung commentedHave removed the unused use statement error from patch #208 in this patch.
Still on 9.2.x.
Comment #210
rafaelaleung commentedRerolling patch for 9.2.x - fixing typo
Comment #211
maxilein commentedWill this EVER reach production?!
Comment #212
wombatbuddy commentedIn addition to the #56 workaround, or if you are using the "number" form element and want to keep the "step" attribute, that you can do it like this:
Comment #214
spokjeUsed
drupal-simple-decimal-validation-2230909-210.patchas base for the newly created MR.Comment #216
maxilein commentedBackground to #212 https://www.drupal.org/project/drupal/issues/2965627
Comment #218
maxilein commentedWorked fine for me on 9.1.7 and 9.1.8
Comment #219
maxmendez commentedThanks for your work, the patch stop working on 9.1.10
Comment #220
maxilein commented@MaxMendez: can you be more specific. I just applied drupal-simple-decimal-validation-2230909-210 without problems to 9.1.10.
Comment #221
maxilein commentedThis error occurs on a content type adding decimal field or looking at the field's configuration:
Comment #222
dpw commentedHello, here's the output when I try to apply the patch from #210 to 9.1.10. Needless to say, I have a valid decimal number that receives a "not a valid number." error message.
edit: choosing to convert the fields to integers.
Comment #223
maxmendez commentedThanks @dpw for the output.
I've worked to make the patch #210 works on Drupal 9.2.1, the main problem was on NumberFieldTest.php.
I've fixed the problem on NumberFieldTest.php and the notice reported on #221.
Be free to test and improved if it is necessary.
Comment #224
kkasson commentedWe've been using the patch for a while and it has helped but I just found another case that's still failing.
I have a decimal field with with Precision 20 and Scale 5. Validation fails for numbers above 2914543744, e.g. 2914543800 will fail but 2914542000 is okay. I'm not completely sure but I think it has to do with the floating point the conversion of $step, i.e. PHP converts step from 0.0001 to 0.00001000000000000000081803053914031309545862313826
It starts working again at 68719476737, that one is valid.
Comment #225
maxilein commentedThis seems to be a never ending story. We should ask if this is solvable with current means at all.
May I ask why there is no library in the core/vendor doing all the decimal handling?
Or a specialized extension:
https://php-decimal.io/#introduction
Read more about it here: https://medium.com/@rtheunissen/accurate-numbers-in-php-b6954f6cd577
Could this be a more reliable approach?
Comment #226
andypostThere was support for bcmath and gmp in early core but afaik only drupal commerce require bcmath
Moreover PECL decimal is hard to require on shared hostings
Comment #227
kevin.brocatus commented#223 no longer applied to Drupal 9.2.x.
Comment #228
kevin.brocatus commentedFixed some issues with #227.
Comment #229
maxilein commented#227 Seems to work on 9.2.4. Thank you!
Comment #230
bsztreha commentedI have to apply this patch against 9.2.5, so I created a modified version from #228 to make it apply.
Comment #231
maxmendez commentedThanks @bsztreha, I've applied the patch #230 without problems.
Comment #232
maxmendez commentedUpdated patch to work on 9.2.7.
Comment #233
maxmendez commentedUpdated correct patch
Comment #234
bsztreha commentedThanks @MaxMendez for updated, but seems you left
/webin patch file and I couldn't apply the patch, uploaded the version without extra dirComment #235
maxilein commentedIs there a reason why you left out so much functionality from 228?
Diff between 228 and 234:
Comment #236
maxilein commented#234 applied fine with 9.2.8. Thanks
Comment #237
maxilein commented#234 applied fine with 9.2.9. Thanks
Comment #239
maxilein commented#234 applied fine with 9.3.0. Thanks
When will we finally set this into production?!
Comment #240
jbowm2 commented#234 applied clean and worked for me! Thanks
Comment #241
rgristroph commentedI applied #234 and it fixed the problem for me ! Thanks.
Comment #242
maxilein commented#234 applied fine with 9.3.6.
When will this finally get into core?!
Comment #243
andregp commentedPatch #234 Applied cleanly on 9.4.x and solved the issue (I was able to save decimal numbers that were returning errors before the patch (Following IS steps to reproduce)).
Setting to RTBC as per #236, #237, #239, #240, #241 and #242, who also tested the patch.
Comment #244
jwilson3Afaict, there is a valid question on #235 that should be addressed:
At least some justification about why the removals are Okay.
All the additions to core/tests/Drupal/Tests/Component/Utility/NumberTest.php should at least be added back to the patch to confirm that we're passing the expanded test cases...
Comment #245
andregp commented@jwilson3 You're right,
I couldn't find a reason for this code being removed. I tried adding it to the code and it even corrected the 2 failed tests on patch #234 (My bad for changing it to RTBC) =/
I'm working on this now.
Comment #246
andregp commentedBellow is the new patch with the expanded tests added back.
I also fixed some small coding standard errors in the patch code, to ensure no CS errors would be introduced by it.
I also removed some changes introduced by #183 because:
1 - They are outside this issue's scope so shouldn't have been added here in first place.
2 - These deprecated lines were already fixed in between core commits (as you can see between the patches #228 and #230).
I'm also adding a tests-only patch (which is expected to fail).
Comment #248
andregp commentedtests only patch failed as expected :)
Comment #249
samaphpGreat effort. Thank you everyone. I can confirm the patch in comment #246 is working well.
https://www.drupal.org/files/issues/2022-03-10/2230909-245.patch
The numbers that was failing before is working fine. I was trying to save (201500000.00) and now i can save it with no issues after applying the patch. I checked the DB and the node view page all working fine.
Actually it is great to compare the float number instead of the exponential number that was failing.
TESTED on Drupal 9.3.7
Comment #250
alexpottReturn type hint of
: stringReturn type hint of
: intIs this necessary - I'm not sure it is. It appears unused. Yet we're listing it as an API change. I think we need to fix up the issue summary to reflect the current direction. If the issue summary is correct then we'll need a change record. We might need a change record anyway.
Comment #251
andregp commentedRemoved #number_type mention on the IS.
Changed patch as per #250.
Comment #254
andregp commentedThe test fail seems unrelated, requeueing tests.
Comment #256
andregp commentedOkay, I still suspect the test fail is unrelated (see bellow), but it's odd that the same supposedly random error happened twice:
Requeueing one more time (I hope it's the last).
Comment #258
andregp commentedOkay, it seems the error is indeed related to the patch (three times the same error!), but this is weird.
I don't see a connection between the changes on patch #251 (see interdiff_2230909_245-251.txt) and the error returned by the bot (see #256).
Comment #259
maxilein commentedIn the meantime: https://www.drupal.org/files/issues/2022-03-10/2230909-245.patch applied fine on D 9.3.11
Comment #260
andregp commentedHA! The fail is unrelated!
I just got another of this exact same fail on a previously passing patch from another issue (see here https://www.drupal.org/pift-ci-job/2364559), the HEAD seems to be broken (see here https://www.drupal.org/pift-ci-job/2364550).
Sending back to needs review. This time I'll wait for the HEAD to pass to then retest.
Comment #261
maxilein commentedApplied fine to D 9.3.12
Comment #262
maxilein commentedApplied fine to D 9.3.13
Comment #264
xjmComment #265
maxilein commentedIn the meantime: https://www.drupal.org/files/issues/2022-03-10/2230909-245.patch applied fine on D 9.4 and D9.4.1
Comment #266
darvanenApplying the patch in #251 fixed this issue for me.
I have also:
I reckon this is RTBC.
Somewhat related: #2965627: Bad step value with decimals listed this issue as a possible duplicate but I could not reproduce that issue and have closed it as such.
Comment #267
larowlanPHP does not guarantee .... what?
Don't leave me hanging!
jokes aside, this sentence seems like its missing a resolution, what does PHP not guarantee?
suggest 'discard the not guaranteed ones' => 'discard those that are not guaranteed'
count => counted
'or a float formatted' => 'or formatted as a float'
I don't think the word 'many' adds anything here
should we be doing something like an is_numeric check here?
I think this function would return 3 for "Yep.yep"
Would we return 0? or perhaps throw an invalid argument exception?
we can use str_contains here now, we have a polyfill for lower PHP versions
is this missing 'if' before 'it'?
If precision is less than 10, should we allow 10?
I was going to suggest that we should be able to test this with a kernel test by creating an entity and calling ->validate on it, but it looks like this is only used in form validation. The constraint added to DecimalItem does not validate precision and scale, so that means API clients don't get the same validation as form submissions, which is a bug. Can someone search to see if we have an existing issue for that, and if not create a follow-up? We shouldn't be validating this in a form validation callback.
Yep, we need some non numeric values here to test they return 0, or even better, throw an exception
Comment #268
pooja saraah commentedAttached patch against Drupal 9.5.x
Addressed the point 1,2,3,4,5,7,8 on #267
the Point 6,9,10,11 needs to be worked
Comment #269
smustgrave commentedAddressed issues 1-9 + 11 from #267 only one I need a little guidance on is 267.10 not sure what to best search for to find that issue.
Comment #270
darvanenOpened #3300348: Decimal precision and scale are not validated when submitted via API as a follow up to cover 267.10, no need to postpone either issue in my opinion.
Comment #271
jaime@gingerrobot.com commentedThankyou Smustgrave #269 works for me for saving integers that have no min value on the fields on Chart Module. Also able to save the field length on a new text field on a node that was default 255 then wouldn't save.
Comment #272
smustgrave commentedIf it solves your issue can you move to RTBC and let’s see if we can get it committed!
Comment #273
jaime@gingerrobot.com commentedComment #274
maxilein commentedPatch works fine for me.
Comment #275
tgoeg commented#269 applies to 9.3.21 cleanly as well and works as expected here.
Please merge this, this is a major bug that's been open for 8 years now, had several rounds of reviews and tests and the solution in #269 is definitely better than the current state in core (if not the final fix).
Improvements for corner cases can be added later anyway.
Thanks for all your efforts!
Comment #276
maxilein commented#269 applies to 9.4.5 on php 8.1 as well.
Comment #277
vinaymahale commentedPatch #269 works for me on D9.5.x-dev.
Before applying patch:
After applying patch:

Looks Good to merge!
Comment #278
quietone commentedThanks everyone for working on this. This looks quite close but I noticed something that still needs to be done.
There were changes requested to the patch in #267. There have been two patches since then, to address those issues. However, there has not been a review of those changes. We should have confirmation that all those changes were made and they are correct. Setting back to NR for a code review of the patch.
Comment #279
xjkwak commentedPatch #269 works for me too on Drupal 9.4.6, however, it crashed when I added more digits than the set for the field. I fix it by adding a custom mix and max for the field:
Comment #281
LionKingMC commentedHello everyone,
Thanks for trying to solve this important issue.
For those looking for a solution away from having to use patches, I found a work-around by using a module called https://www.drupal.org/project/fraction. It solves the issue by storing the numbers as fractions in the background with the option of working in decimals. It works for me building a website needing numbers having 8 numbers to the right of a decimal.
Comment #282
nod_Patch doesn't apply to 10.1.x
Comment #283
pradhumanjain2311 commentedFix Patch #269 for 10.1.x.
Please review.
Comment #284
smustgrave commentedThank you for the patch but please upload an interdiff so we know what's changed.
Comment #285
pradhumanjain2311 commented@smustgrave
Actually the patch was unable to apply on 10.1.x that's why i changed the files manually.
Comment #286
smustgrave commentedYou should still be able to supply an interdiff.
Rerolled #269 for D10.1
Putting in NR if someone can confirm the issues in #267 have been addressed per #278 request.
Comment #287
smustgrave commentedApologize @pradhumanjainOSL believe you are correct.
Comment #288
fenstratI've reviewed the changes requested in #267 as implemented in #269 and they all make sense to me.
The only thing #269 left outstanding was about the validation via the API, but that now has a follow up: #3300348: Decimal precision and scale are not validated when submitted via API.
When reviewing I found a minor 80 char line wrap issue, attached fixes that.
I hit this when storing map lat/lng values with precision 10 and scale 8. The changes here allow field values to pass validation.
Comment #289
gaurav-mathur commentedApplied patch #288 successfully on drupal version 10.1.x,refer to the screenshots.
Thank you
Comment #290
fenstratMarking as RTBC, which I should have done in #288.
Comment #291
quietone commentedI have not reviewed the code, just the comments.
The comments refer to the default precision for PHP and also an example precision, 'for example, 15'. I am not convinced that that is needed in the comments. I think they can be moved to being mention in an example.
The string 'the precision guaranteed by PHP' could be improved. Since the precision can be set in the ini file we should say it is a configuration value. Something like 'Floats are limited to the precision set in your PHP configuration'.
This should be an @see in the correct place in the doc block.
See https://www.drupal.org/docs/develop/standards/php/api-documentation-and-...
s/If this/If it/
The last sentence is needs work to make it easier to read. I am just too tired right now to make a suggestion.
Not sure this is needed. It is repeating the information in the summary line of the doc block.
s/Number/The number/
These are example and should be in a clear example sections and using @code and @endcode
What does 'step' mean here?
It can be <= 0, it is just the the method returns FALSE in that case.
Comment #292
ameymudras commentedAddressed the issues in #291
Comment #293
smustgrave commentedSeems points in #291 have been addressed.
Comment #294
alexpottI think we need a update function here for existing fields as we're changing the maximum so current configuration can be invalid. Not sure what we should be doing with existing decimal fields...
Comment #295
kiwimind commentedHaving come across this issue on 9.5.8, applying the patch from https://www.drupal.org/project/drupal/issues/2230909#comment-14626332 has solved this issue.
Prior to the patch we were unable to set a default value of a trillion (1 with 12 zeroes) on a 15,2 decimal field. After the patch it has saved as expected.
Comment #296
andypostChecking requirements should go to report/status instead of runtime
If you need properly configured server no reason to scatter this checks all over codebase
Also a test should use permutations for precision configured in field and via ini, I mean missing case when ini_get returns wrong data or it <10
Comment #297
andypostAlso this code should be overridable from contrib to allow override math via PHP
bc,gmp,decialextensionsComment #298
pwolanin commented@anypost - this important patch has been waiting for 7+ years to get committed. we should not expand the scope. If you want to e.g. be able to use BC math, please open a follow-up issue.
Comment #299
samaphpComment #301
vinaymahale commentedAny more work need to be done here?
Comment #302
smustgrave commented#294
and #296 or follow up least opened.
Comment #303
kopeboyI'm not an expert developer and couldn't read all the comments and patches, but I hope this can help.
I was appalled to see I couldn't save web3 wallet balances to my Drupal site (there a lot of decimals places) even if I could easily define my "balance" field (in my custom entity's BaseFieldDefinition) with precision = 65 and scale = 30 (which correctly creates a column in the database with Type: DECIMAL, Lenght: 65,30)...
These values will come from APIs, but while testing with a manual form input (or even by setting the value programmatically though an ECA plugin) a value of
0.123456789012345678901234567890would be magically converted to0.123456789012350000000000000000in the UI AND database (without validation warnings).So I checked
core/lib/Drupal/Core/Field/Plugin/Field/FieldType/DecimalItem.phpmyself and I found that I could fix the problem just by commenting lines 122 to 127:Note that scale should be already defined by this (right?):
So why do we want to round at all when we are already forcing the precision and scale in the field (storage) definition?
From https://www.php.net/manual/en/function.round I immediately read
,
but this is a decimal!!
Then shouldn't that be just an optional Manage Display's setting for decimal fields?
Also, why isn't the round function called for float number fields (FloatItems.php file in the same folder) instead?
Comment #304
tgoeg commentedAlso not a dev, but I think I can at least clear up things a little.
First, this is not a decimal (as strange as it may sound). Your number is a float (mathematically) and SQL stores it as an exact data type
DECIMALwith a given precision (which in fact is a float). There is an explicitFLOATdata type in SQL, but that is not guaranteed to be exact.The basic underlying problem is that computers "think" binary and not decimally. Representing an arbitrary number with lots of places after the decimal point will inherently produce errors (depending on which number it is, as some ("machine numbers") map cleanly to binary base and some do not. That's why people here see this only with select numbers). See https://en.wikipedia.org/wiki/Round-off_error for details.
The internal representation of a number 0.123456 might be 0.123456789 because of rounding errors. That's why rounding to the exact given scale does make sense. It's no actual rounding of the given number in the human sense. It just makes sure the number is exactly the one entered and has exactly that many decimal places as configured.
Does the patch in #2230909-292: Simple decimals fail to pass validation not fix it for you? That would be an important information.
Comment #305
asad_ahmed commentedI have made the changes as per comment #294. And write a update function in Field.install file but i am not sure where to place it, currently i have placed Field.install here core/lib/Drupal/Core/Field/Field.install. Please review it and give feedback if any. Thanks
Comment #306
maxilein commentedpatch #305 applies to D10.2 but it does not work.
I tested a default decimal field (10,2) and a large decimal field (14,2)
Problem 1: Both act like a standard field with 10 digits.
Problem 2: the decimal separator is counted as one digit!
Problem 3: There used to be larger numbers possible than 14.
Please see attached screenshots.
Comment #307
maxilein commentedI just realized that #305 is just an addition.
#292 does not apply to 10.2
Comment #309
szeidler commentedRerolling #292 for Drupal 10.2
Comment #310
maxilein commented#309 helps to use large numbers. Thank you very much!!!! We can upgrade to 10.2!
If we get out of bounds there is just a nasty crash error. Not a warning in the gui.
1,123,123,123.00 - just ok.
123,123,123,123 -> crash
Comment #311
weseze commentedSame experience as maxilein.
The patch fixes our problem for storing correct numbers.
But if we try and enter bigger numbers we get a nasty WSOD and some SQL out-of-bound errors. Not a warning in the interface.
Comment #312
keszthelyi commented@maxilein, @weseze There is another issue and patch for the exception thrown when the entered value is too big: https://www.drupal.org/project/drupal/issues/1003692
Comment #313
apolitsin commented💥 Friends!
🎉🎉🎉🎉 10 years to this wonderful issue! 🎉🎉🎉🎉
🔥 Created 2 Apr 2014 at 00:19 MSK
Congratulations to everyone on this important event, especially parent #1 @exlin
☀️ Good day, everyone.
Comment #314
mstrelan commentedI guess that's supposed to be sarcasm. Seems that we need to take 309 and add the test from 305 and review against that. A merge request would probably be helpful. The hook_requirements in 296 hasn't been addressed either I don't think.
Comment #315
monaw commentedi am seeing the same problem in D10.2.5...is there a solution/fix yet?
Comment #316
anybodyI can sadly also confirm this core bug which basically makes using decimal fields with a default value impossible.
I'll ask our team if we can invest some time in the plan written in #314! :)
Comment #319
grevil commentedhttps://git.drupalcode.org/project/drupal/-/merge_requests/579 still has their target on 9.2.x and can be removed safely.
New MR targeting 11.x can be found here: https://git.drupalcode.org/project/drupal/-/merge_requests/8282.
Comment #320
grevil commentedI think you meant the update hook provided in #305? Unfortunately, the update hook from #305 is pretty much a template implementation and won't work in its current state. I'll give it a shot.
I'd agree with @pwolanin in #298 on this topic:
Any further work on this should be done in a follow-up issue.
Comment #321
grevil commentedLooks like everything works as expected! Let's wait for the tests to succeed.
Here is a static patch of the current MR for the time being (which also applies to 10.2.x, 10.3.x and 10.4.x).
Comment #322
grevil commentedTests are all green now! Ready to get reviewed. 👍
Comment #323
needs-review-queue-bot commentedThe Needs Review Queue Bot tested this issue. It no longer applies to Drupal core. Therefore, this issue status is now "Needs work".
This does not mean that the patch necessarily needs to be re-rolled or the MR rebased. Read the Issue Summary, the issue tags and the latest discussion here to determine what needs to be done.
Consult the Drupal Contributor Guide to find step-by-step guides for working with issues.
Comment #324
anybodyNice work! Just gave it a try and at least I can now save the numeric fields I wasn't able to safe before! :) YAY
Sadly the update hook still needs some work:
The SQL storage cannot change the schema for an existing field (field_example in commerce_product entity) with data.
Comment #325
grevil commentedAlright, the field storage should be updatable now, through the in 10.3.x newly introduced "updateFieldStorageDefinition()" method!(https://www.drupal.org/node/3397892)
Please review (no idea why the review bot set this to needs work, it definitely applies on 11.x).
Here is the new static patch (Update hook won't work for Drupal < 10.3.x).
Comment #326
smustgrave commentedProbably because there is a 9.2 MR and a mix of patches when probably should just be a single MR and no patches
Comment #327
maxilein commentedThanks for all the sudden speed!!
I'd love to help by testing it.
Why are the automated tests only limited choices ( e.g. 10.1.x) ?
Is this already testable?
Comment #328
grevil commented@maxilein yes, this is testable if you are using 10.3.x! Just apply https://www.drupal.org/files/issues/2024-06-04/2230909-325.patch through composer! :)
@smustgrave probably... I don't have the permission to close https://git.drupalcode.org/project/drupal/-/merge_requests/579, unfortunately. Anybody with proper permissions, feel free to close it in favor of https://git.drupalcode.org/project/drupal/-/merge_requests/8282.
Comment #329
maxilein commentedSorry. I am still on 10.2
I don't have 10.3 yet
Comment #330
phthlaap commentedThe HOOK_field_widget_form_alter has been deprecated, I think the issue summary needs update.
https://www.drupal.org/node/3180429
Comment #331
jcnventuraNo need to set this to "Needs work" if the workarounds in the issue summary are starting to get outdated. The workarounds are not really part of the issue, and are only there because of our failure to fix this in the last 10 years.
Comment #332
smustgrave commentedCleaning patches for clarity
May need a rebase as the test-only feature is failing to run.
Left some small comments.
Comment #333
smustgrave commentedAppears to have consistent Unit test failures.
But test-only feature is now running and showing coverage!
Comment #334
alfthecat commentedJust pitching in with my report: after patching Drupal 10.3 I have no more issues with large numbers with large decimal scales. Thanks everyone!
Comment #335
maxilein commentedMR !8282 applies to D10.3.1.
But the new max value seems a bit low.
I have a decimal field with precison 20 and the update_hook fails on it.
Is there a way to force the update?
I also tried to use this module: https://www.drupal.org/project/precision_modifier
But it is not working (I opened an issue https://www.drupal.org/project/precision_modifier/issues/3460201 )
Is there a way to force this update?
Comment #336
maxilein commentedI am confused about the max size of the decimal numbers.
Why do we limit fields to 14?
Here https://www.drupal.org/docs/7/api/schema-api/data-types/decimal-numeric it says:
MySQL also says it uses 65 digits.
https://dev.mysql.com/doc/refman/8.4/en/precision-math-decimal-character...
Comment #337
anybody@maxilein Re: #336: Have you read all the comments above? That would be important.
Especially see #200!
My impression is, that this change is made to align the database schema max with the max php precision value?
Floats are complex, so this should definitely please be checked by someone who digged into this in the recent past. E.g. see https://stackoverflow.com/questions/14587290/can-i-rely-on-php-php-ini-p...
Comment #338
maxilein commentedThank you Anybody. I really missed the setting in the php.ini
My question was: if the database gives us a specified DECIMAL column with a precision of 65 digits - why would we limit ourselves to 14?!
That does not make any sense. Even if it is the default precision limit of php.
Maybe we should think of an informative check on the Status page - just to let anybody more easily understand the situation:
echo "The max float precision of you system is: " . PHP_FLOAT_MAX;
Comment #339
maxilein commentedTesting the php.ini precision value with larger precision.
I have a decimal field with a precision of 20 scale 2.
(It dates back to when there was no limit by this patch). It has worked ever since.
And this is what confused me in the first place. I have a field with precision 20 while the php limit is 14.
But the field works within the limits 14 (even though has larger precision)
1) When I use the 20/2 field with a default php precision of 14:
I can enter numbers within the limits of 14 and I can enter something after the decimal point whith other digits than zero. Everything is working fine.
2) When I increase to a default php precision of 24/2 (without changing the field):
I can add save large numbers according to the 20/2 settings of the field: 123.123.123.123.123.123,00
BUT there is a bug: I can not enter other digits than zero behind the decimal point on ANY decimal field:
eg. 123.123.123.123.123.123,01 -> is not a valid number.
Even on other fields with default precision of 14 and when the numbers are really small (e.g. 890.34)
Comment #340
maxilein commentedAnother observation when precision on the site is set to 24 (instead of the default 14). It probably has nothing to do with decimal fields, but maybe someone of you can think of other potentially unsafe situations.
False Status Message: Incompatible with PHP 8.3.6: https://www.drupal.org/project/diff/issues/3462791
the comparison from 8.3 in the info.yml file of the module is compared to a float and fails.
I have not yet get a feedback of the module developers. So I can't say how they do that comparison.
Comment #341
drupalfan2 commentedAttached is the same patch as in #325 but with unix line endings.
Needed to be able to use the patch with an older version of the unix patch command line tool.
Comment #342
grevil commented@phthlaap what is up with MR 579? Seems quite broken to me?
EDIT: Ah I see, it's on the same branch but against 9.2.x, that won't work, and we already have an existing one, which can be easily backported to 10.x.
@maxilein I think we should create a follow-up issue concerning the higher precision issues. Most people will stay at the default PHP precision level.
Comment #343
anybodyRe #3
That's possible if we can be sure that the fixes from this issue are non-destructive. This should not break anything. Otherwise it has to be solved here.
Comment #344
grevil commentedChanges by @phthlaap actually introduced new phpunit test failures. I reverted them again.
Comment #345
grevil commented@smustgrave the unit test failures were introduced by unrelated changes done to the issue branch. The only failures left now are functional javascript failures. And I am fairly certain these are unrelated:
At least, I see no point on how this is caused by the changes done here.
Comment #346
grevil commentedHa! I knew the test failures had nothing to do with this patch. Just some 11.x shenanigans going on. Rebasing fixed the tests.
Please review!
Comment #347
anybodyRTBC for MR !8282 once again, thank you @Grevil for bringing this right on track again!
@phthlaap please stop these destructive changes.
Comment #348
maxilein commentedI think we must make sure that the following
is not caused by this patch.
After all it validates the numbers and I think it is very probable that the error is caused by some routine of this patch.
And if not: it still seems like a matter of validation to me.
Can someone point us to the code where this is done?
It is very easy to reproduce this bug:
Set precision to something like 24. Restart Webserver. You don't need to change or create a field. All decimal fields are affected.
Just try to add a digit (on an existing decimal field) behind the comma separator other than zero.
Comment #349
grevil commented@maxilein sorry, it sounded like this problem was only reproducible with manually adjusting the precision in the php.ini. But you are correct, this is reproducible with the default precision value of "14":
Steps to reproduce
Input / Output outcome
123123123123,01 is valid, and the entity can be saved without any problem.
123123123123123,01 is not valid and saving the entity will result in an error message saying:
123123123123123123,01 is not valid and will result in an EntityStorageException:
Back to needs work again. We also need new tests reproducing the beforementioned issue.
Comment #350
maxilein commented@grevil. Yours is just an ugly unhandled but expected error. You tried to save a value larger than precision 14.
My bug is different: it is only about the 2 digits behind the comma (,00).
If default precision 14 is set you can save digits other than zero behind the comma.
If precision is larger (e.g. 24) you CANNOT save digits other than ,00 behind the comma. That is the bug.
I suppose there is some routine in this patch evaluating precision wrong therefore dropping the evaluation of digits other than ,00.
Comment #351
grevil commented@maxilein no. The precision value is
(https://www.php.net/manual/en/ini.core.php#ini.precision)
All the numbers I used for testing use a precision of 2.
And as I said in #342, we should create a follow-up issue for any problems with the decimal field when using a higher precision than "14", since in 99% of all cases you don't need a higher precision value.
Comment #352
maxilein commented@grevil: you are mixing precision and scale.
The bug is messing up the scale validation.
And that must be connected to this patch. So it cannot be too difficult to look at it now.
I cannot find the code for it. That is why I asked for help to point to that.
Comment #353
anybodyThis is the largest rabbit hole I've seen for a long time! ;)
There's another issue: If you have an existing decimal field with for example a precision of 20 you can't save changes to the field anymore, as there's a form validation error:
which is totally correct, but you're just locked in!
Trying to change the precision in the
field.storage...ymlas a hacky workaround is refused by the system bywhich is also totally fine and correct, but makes the lock-in even harder.
Any ideas?
Maybe we can at least find a way to first fix the user-facing issues and afterwards do further clean-up?
This is a really problematic thing for Drupal as a framework for "simple" (haha) numeric fields! and we already have 10Y and 352 comments. Users being new to Drupal and running into this will be quite confused and unsettled...
Update: Setting
php_value precision 20for php.ini was the only workaround that made it possible to save the field for me.Comment #354
maxilein commented@anybody it makes sense to only be able to update fields within the bounds of the set precision of your site.
In the past - before this patch - it was possible to create larger (>14 precision) fields. That is probably how you ended up with them. That is how I ended up with them.
I would the say the behavior is as intended and makes sense.
If you want to change a precision for an existing 20 field. Set php.ini to 20. Then use this module to rescale your precision. Then afterwards adjust your php.ini back to the intended size.
Also if your field is larger than default 14 or whatever you have set in your php.ini - you can save values to this (lager than the value in php.ini) field without problem as long as the value you are saving does not exceed the php.ini default precision. E.g. you can save a value with precision 14 into a field which has precision 20. Just the values that are transported to the field will never exceed the php.ini value.
Comment #355
hoporr commentedPatch #325 and #341 fail on an upgrade from
core 10.1.3
to
core 10.3.10
with the following message:
Do you wish to run the specified pending updates? (yes/no) [yes]:
> yes
> [notice] Update started: field_update_10001
> [error] The SQL storage cannot change the schema for an existing field (field_XYZ in node entity) with data.
> [error] Update failed: field_update_10001
[error] Update aborted by: field_update_10001
[error] Finished performing updates.
The patch had been run before on the 10.1 site, so in theory the storage was already changed. Which is strange, because in the update routine (in the patch) there is a check for "if there is a change", and the routine only runs then.
What could cause this?
Do you think the jump in version is too big (10.1 to 10.3)? Should I upgrade to an intermediate version, like 10.2.x first ?
Comment #356
hoporr commentedThe reason why the patches fail on 10.3.10 is because of this new core issue:
The SQL storage schema for an existing field can be changed
https://www.drupal.org/node/3397892
The install routine in the patch needs this additional line so that it can run:
$decimalFieldStorageEntity->setSetting('column_changes_handled', TRUE);
Comment #357
anybody@hoporr thanks! Could you incorporate that into the MR please?
Comment #358
anybody@alexpott could you or anyone else with the appropriate permissions please close the old MR!579 (9.2) to not confuse people? I don't have permission.
Thanks!
Comment #359
hoporr commentedPatch #341 updated with the additional line in "install":
$decimalFieldStorageEntity->setSetting('column_changes_handled', TRUE);
runs OK on my 10.3.10 system
Comment #360
anybodyThanks @hoporr I incorporated that into the MR.
Comment #361
hoporr commentedThere is another issue on the 10.3.10 systems.
The above patch allowed the "install" function to update the field setting, and change the precision of the field.
However, now on the "status" page, you get a bunch of warnings like this:
Mismatched entity and/or field definitions
The following changes were detected in the entity type and field definitions.
* The node.field_XYZ_val field needs to be updated.
* The node.field_XY_2_val field needs to be updated.
The normal way to solve this is to go into the edit screen of the decimal field and just save it again.
However, there the precision is now set to 16.
And the entity system throws this error:
Precision must be lower than or equal to 14.
So you cannot re-save the field with that higher precision.
It seems like somewhere we now would have to change the entity system as well, so that longer precision are not throwing this error?
Any ideas ?
Comment #362
hoporr commentedFurther on #361:
Interestinly: I went into another drupal 10.3.10 system, without this patch, created a new digital field and was able to crank up the precission past 14 [to 16] and the system did not complain.
Which makes me think that this "limit to below 14"somehow comes from this patch?
I do see this comment in the patch:
+ * The value to normalize. If its a string, it must be formatted as an
+ * integer or float. Float with a higher number of significant decimals
+ * than the precision value from the PHP runtime configuration
+ * [default: 14] will lose the additional precision as PHP does not
+ * guarantee that precision value.
so the 14 seems to be important.
Is there anything in this patch that may cause the entity system limit the precision it to 14, whereas standard drupal does not?
Comment #363
hoporr commented#361, #362 continued.
This issue will only occur if you had the fields set before to a value larger than the system "precision" max.
In our case, we have precision of 16, and that appears to be larger than what php can safely store (per comment in the code it is 14).
So, looking at the code, the "field_udpate_1001" should have caught this and reset those fields to that value 14.
Apparently it did not, so something else must still be going on...
I'll check deeper the next few days.
Comment #364
hoporr commented#363 continued
I did some more digging. In our patches, the hook_update_n 10001, exists to reduce the the precision down to the system PHP max precision settings, which is 14 digits. In our case, we indeed had some fields that were 16, so the hook_update tried to reduce this.
I did find a solution for this, but I am not familiar enough with the whole config system etc to suggest that this would work for all. I would ask some of the other developers to review this before this should go into some patch.
In the current patch, we use this code to update the precision, which is also the correct suggested way here:
https://www.drupal.org/node/3397892
$decimalFieldStorageEntity->setSetting('precision', $newPrecisionValue);
$decimalFieldStorageEntity->setSetting('scale', $newScaleValue);
$decimalFieldStorageEntity->setSetting('column_changes_handled', TRUE);
// Update the field storage definition:
\Drupal::service('entity.definition_update_manager')->updateFieldStorageDefinition($decimalFieldStorageEntity);
However, I found that the patch ran without errors, but the value of the precision did not change. That is, the update does not do what it is supposed to do.
I then found another module called "Precision Modifier" and looked how they do it:
For a given field:
1) Grab the data in the DB tables, and store it
2) Empty the Table
3) run code to change precision (other code than what is in the patch)
4) Reinsert the data
I did not feel comfortable with "taking out the data in the tables", so I tried to do it without.
So I wrote a hook_update_n in one of my own modules, took the code from the current patch for the update, and replaced the part in the loop/if-statement that changes the field defintion like this with code similar to the PrecisionUpdate module ( in PrecisionUpdateService)
With this code, the fields were updated correctly (at least it seems so far).
What I found, the part where it presaves the $config was crucial.
After that you did not even have to have the column_changed_handled.
Again, I found this code works (for me), but am not sure if this good for a general solution.
Comment #365
maxilein commentedShouldn't this patch respect the setting of the php_max value of each system?
Maybe I misunderstood you. But for some people a precision of 14 may not be enough. E.g. scientific data ...
I have mentioned this before: https://www.drupal.org/project/drupal/issues/2230909#comment-15732574
And I don't think that the field's UI respects the backend setting of php max value.
Comment #367
keoal commentedI am experiencing this issue with Drupal 10.4.5 is there a patch available for this version. I have tried to apply the other two but they won't work.
Comment #369
heddnA couple things here. 1) We shouldn't do hook updates that look at environment specific changes. Localdev vs live site likely have different php.ini settings. We shouldn't depend on them to be the same. 2) Why do we care what the display precision is set to for data storage? We shouldn't.
I've removed the hook update.
Comment #370
heddnI streamlined the approach here a lot. This issue is all about validation. So changes to widgets shouldn't be in scope. And the storage of the data should be limited by the DB engine not by the configuration of PHP. So a lot got stripped out. If we need to address those things, they should happen in a dedicated issue.
Comment #372
godotislateMR failed tests.
I added some comments as well. I think the biggest issue on the MR is that there is a confusion between "precision", which is documented as the number of significant digits, (the number of all digits before and after the decimal), vs "scale", which is the number of digits after the decimal.
Comment #373
maxilein commented#372 I completely agree.
And I don't understand this from #370 "We shouldn't make this field value dependent on an environment specific setting. It needs to be distinct."
We have three layers:
Database - php - drupal/gui
Why should they be distinct from each other?!
if php sends something too large for the db -> error.
If drupal sends something wrongly scaled -> error.
...
In my opinion we should specify rules on how they need to be in sync to each other.
Comment #374
heddnRe #373: sorry about my poorly worded and unclear statement there. I'm not sure how to word it but making something like this dependent on a PHP ini settings, which is regularly set differently on localdev vs other environments seems wrong. This is the type of thing we would regularly do a hook_requirements around if it were a feature of php for image quality or a feature of mysql for data storage. But we do nothing like that here.
To add to that though, this precision value is something we can control ourselves via an ini_set at runtime. If we have a certain value configured for the field, then we should set the value before invoking it.
Comment #375
maxilein commentedThank you.
Let me explain my observation - an outside view so to say.
I added the field decimal, because I needed a place for huge numbers with not so many positions behind the comma.
Then I had to realize that a field called decimal is very much limited in comparison to what I expected, or from my experience with other decimal functionality in other system would have expected.
That was my confusion number one.
Then I learned I can configure the field upon its creation. And before this validation bug here - or better without realizing that again - it was not problem to set the gui to whatever I needed. A huge number with a few digits behind the comma.
Entering data caused numbers to be crunched ... we all know that story here.
Then you wonder what to do and how it all depends on each other.
The default database allows for much more digits before and after the comma than default Drupal.
In my opinion that is not good. It is confusion number two.
Then you realize that also php may have a limit inbetween. That is confusion number three.
In my opinion we should make that clear somehow.
I think Drupal should ask during install how the user wants it to be set.
e.g.
DB limit is ...
php limit is ...
Drupal default is ...
Would you like to use Drupal default precision/scale for decimal fields or would you like to increase the default to the php and DB limits.
Then at least it would be transparent what happens here.
The next problem is the intransparent limitation of this patch. I can set some precicion/scale during field creation and it may crash because of underlying system differences ... which were not obvious during field creation.
Why should this patch not be adjusted to system parameter settings? it does not make sence to me to keep an artificially limited number of digits before or after the comma if the system already provides for that bandwidth.
And btw: thank you all very much for your efforts!
Comment #376
heddnOur logic around how we use https://www.php.net/manual/en/ini.core.php#ini.precision to get a number is also in error. Its value can be negative 1 (-1). Which really messes with things if we assume it is a positive length to use with
number_format.Comment #377
nicholassPatch failing, do we just need to merge the latest 11.x into this branch?
Comment #378
joevagyok commentedI re-rolled the contents of the MR over the 11.2.x branch. Currently fails to apply over 11.2 release. Added no interdiff, since there was no change to the implementation itself, only resolved conflict with the new target branch of 11.2.x.
Comment #379
keszthelyi commentedFor core versions below 11.2 our projects are still using the patch from #309. However, that patch is not applying with every patching methods (it is failing for example when used with cweagans/composer-patches version 2). The reason is that since the patch was published, one of the test methods got a void return type. This new patch is a reroll of #309 that adds that void return type.
Comment #380
maxilein commented#378 works for me. Thank you
Comment #381
chewie commentedAdded rerolled patch for 10.5.3 core version. Normally, it should work for other versions, but I've tested only with the mentioned.
Comment #383
someshver commentedI have re-rolled patch #378 for the Drupal core version 11.2.4.
Comment #384
julien tekrane commentedThank you @someshver
It works for me in 11.2.4
Comment #385
maxilein commentedThank you #383 also applies in drupal/core (11.2.5)
Comment #386
weseze commentedPatch #383 causes issues for me for "weight" fields that are now invalid. Without the patch this is not an issue.
Comment #387
iaacristobalCan confirm Patch #381 works for Drupal 10.5.4. Decimals above 3500000 now validates properly
Comment #389
spadxiii commentedJust did a rebase on 11.x, but the merge-request still doesn't apply to 11.2.5
Comment #390
spadxiii commentedHere's a patch that should apply to 11.2.5
Comment #391
maxilein commented#390 does not apply to drupal/core (11.3.1).
Comment #392
carlitus commentedSame problem, cannot apply to 11.3.1
Comment #393
nagy.balint commentedRerolled #383 for Drupal 11.3.1
Comment #396
fskreuz commentedComment #397
smustgrave commentedMR appears to be opened against 10.6 but there was already a 11.x
Comment #398
fskreuz commentedThe 11.x diff did not apply cleanly to 10.6.x, so I created a separate MR for anyone who wants to use it on 10.6.x.
Comment #399
godotislateLooks like there's been some amount of work done on the 11.x MR since I moved this issue to Needs work in #372, but no one has moved it back to Needs review, and the "Needs tests" tag is still applied.
I think for this issue to go forward to NR, someone will need to check the state of the 11.x MR to check that adequate test coverage is in place and MR comments have been addressed.
Comment #401
maxilein commentedHere is the patch file for #400 which is the migrated #393 (from rerolled #380) to apply to 11.3.2.
It applies for me and large numbers seem to be working - although the pipeline still complains.
Needs work and proper investigation, why it fails.
Comment #403
maxilein commentedNow thinking about better tests:
1. Refined Problems Endangering the Solution (No BCMath)
The string-based scaling approach is fraught with edge cases.
The "Trailing Zero" Mismatch: Value: 1.20 (2 decimals -> scale 100)
Step: 0.1 (1 decimal -> scale 10)
If the logic blindly scales both by their own decimal count rather than the maximum of the two, the math breaks.
Correction required in code: The code must calculate $scale = max($value_decimals, $step_decimals) and apply that same scale to both. If the patch fails to do this, it is broken.
Scientific Notation Failure: If the code looks for a dot . to count decimals, it finds 1.
It scales by 10.
Result: 1.0E-7 * 10 = 0.000001.
This is clearly wrong (should be integer 1).
Risk: The regex used to count decimals must explicitly handle E- syntax, or the logic fails completely.
The PHP_INT_MAX Hard Ceiling
:
If the code calculates the multiplier based on string length or decimal count without normalization:
PHP casts (string) 1e-7 to "1.0E-7".
Without bcmath, we cannot solve the overflow issue. This is an accepted trade-off of this patch. However, the code must fail gracefully (return FALSE) rather than producing a PHP Fatal Error or a silent false-positive.
2. Analysis of Test Coverage & Suggestions
The existing tests are insufficient because they test "happy paths" of the fix. We need "destructive" tests that specifically target the weaknesses of string manipulation and floating-point modulo arithmetic.
This test suite should cover every weakness listed: binary incompatibility, integer overflow boundaries, scientific notation parsing, and sign-logic.
I will put them into:
core/tests/Drupal/Tests/Component/Utility/NumberTest.php
Comment #404
maxilein commentedWhich brings us to the spots that will break by these tests:
While the patch fixes the original reported issue (simple decimals like 20.12345678), it introduces new bugs because it does not strictly handle the technical risks.
1. Scientific Notation Failure
The Code: The patch usually counts decimals by looking for a period (.).
The Flaw:
2. Integer Overflow (The Fatal Error)
The Code: The patch performs math by casting to integer.
The Flaw:
3. Trailing Zero Mismatch
The Flaw:
Comment #405
maxilein commentedThe Code in core\lib\Drupal\Component\Utility\Number.php in function validStep ~ line 117:
seems mathematically unsafe when an offset is involved. It uses a "heuristic" shortcut rule to fail fast if the value looks too precise. However, that shortcut causes false failures in valid scenarios
Counter-Example (The "Offset" Problem): Imagine a form where:
Applying the snippet's logic:
The proposed solution I will present below (it handles "too much precision" without needing this explicit check—if a value has extra, non-matching decimals, the modulo arithmetic will essentially return a non-zero remainder anyway) fixes this: it uses $max_decimals = max($value, $step, $offset). It scales everything to the precision of the most precise element (in this case, the offset 0.25).
Comment #406
maxilein commentedHere are the suggested changes in a patch. (Against the latest MR in https://git.drupalcode.org/issue/drupal-2230909/-/tree/2230909-testing-s... from #401)
I hope you all can review it thoroughly and we can finally put this into production very soon.
Besides addressing the problems discussed above:
It is also handling "Epsilon Neglect" (Floating Point Drift): if a calculation results in 4.00000000000001, a strict integer check often fails. The solution uses a "Two-Layer Defense" to stop this.
Defense A: The round() before (int) (Primary Path) In the standard scaling path, we do not just cast to int. We round first.
This ensures that microscopic floating-point noise is snapped to the nearest correct integer before the modulo check happens.
Defense B: Explicit Epsilon in Overflow Guard (Secondary Path)
If the numbers are too big for integers, we fall back to float math. Here, we cannot use == or %. Instead, we explicitly calculate the distance to the nearest valid step and check if it is within a tiny margin (1E-10).
This effectively tells PHP: "If the difference is 0.00000000001, treat it as zero."
And it is Handling Negative Operand Logic
PHP's modulo operator % acts differently with negative numbers (e.g., -10 % 3 is -1, not 2 or 1). This causes sign-flip errors if not handled.
The solution neutralizes this by calculating the Absolute Distance rather than raw subtraction.
Works because:
By forcing the numerator to be positive (abs), the modulo operation behaves consistently regardless of whether the user is typing negative values or using a negative offset.
Comment #407
maxilein commentedapplied to 11.3.2 just fine. Thank you.
Comment #408
maxilein commentedSince the patch is now on main, there is nothing holding us back testing and reviewing it.
Comment #409
smustgrave commentedMRs need to be cleaned up before review, either closed or hidden.
Also none appear to be passing
Comment #411
rob230 commentedThe more recent patches / merge request cause integer fields to consider that a whole number below 10 is not valid.
Here's why (using 5 as the example):
Result: 1.
Result: 15
countSignificantDecimals(5.0)callsstatic::normalize(5.0), which does the following:This generates the "number" (string):
5.000000000000001Comment #412
rob230 commentedThe patch in #406 (applied on top of previous MR) does not have this problem, it works fine for integers less than 10 and higher precision numbers.
I think that's what's in merge request 8282? I'm applying it to 8.2 and it doesn't apply, but does look like it works if I backport it.
Comment #413
heddnThe last few comments are very confusing. Is all of the work in MR 8282? If it is, why is it still failing red and folks saying it works. Let's get everything added to an MR. Patches against an MR at this point just confuse things.
Comment #418
fskreuz commented- Rebased MR8282 with the latest upstream main branch. All focus should be on MR8282.
- Hid the MR14204 (was for 10.6.x), MR14280, MR14283 (Draft MRs), and the patches to simplify things.
- I could not cherry-pick over the changes from MR14280 and MR14283, they were conflicting when added to MR8282.
- I could not add the patch from #406, it's difficult to tell what it's changing from MR8282.
Comment #419
maxilein commented"- I could not add the patch from #406, it's difficult to tell what it's changing from MR8282."
If you tell me how I could help ...
Comment #420
heddnThis seemed like an excelent chance to have Claude help with re-adding the bits called out from #406 and beefing up test coverage. I've just pushed those changes. TDD is key to this work so I started with that first. Taking off the needs test tag.
Comment #421
berliner commentedPatch for 10.6.x based on MR 8282. Just for folks like myself who can't yet update to 11. The last patch I used based on MR 14204 broke the UI when using tabledrag with PHP 8.4, not sure why exactly, but I did find https://github.com/php/php-src/issues/18266 during my research and attributed it to that. MR 8242 seems to not be affected from what I see in my local setup.
Comment #422
anybodyStill one of the "most major" issues for any Drupal projects using Decimal fields definitely.
Just found #2413843: Drupal Decimal Field should support 'unsigned' setting. and I think not having a setting for UNSIGNED is also part of the (logical) issue.Edit:
My assumption was wrong, I closed that issue WON'T FIX now, as:
https://dev.mysql.com/doc/refman/9.6/en/numeric-type-syntax.html
Comment #423
anybodyI fixed the merge conflicts. @godotislate there are some comments for you by @heddn, could you maybe have a look and reply?
Let's get this one fixed, we ran into it in a further project now, Decimal fields are really broken in Drupal due to this bug.
Comment #424
anybody@heddn all green now!
The MR does not apply against 11.3.x, so I'm unsure what we should do in the meantime. #421 works, but I guess it's different from the MR implementation.
Should we create a separate MR against 11.3.x for the time being or try to get this merged into main and 11.3.x asap?
Comment #430
anybodyAs the current MR against main does not apply again 11.3.x and #421 didn't work for me either (leading to other wrong validation errors) and I needed a solution, I prepared MR!15288 that simply disabled the number validation.
Patch attached until this is finally resolved. USE AT YOUR OWN RISK, KNOW WHAT YOU'RE DOING ;) This may result in database errors if invalid numbers are saved!
Anyway, let's get this one finished finally!
Comment #431
psf_ commentedFor D10.6.3 this work for me: (updated from issue description)
Comment #432
hoporr commentedFor 11.3, the warning by "anybody" about the quick patch sounded ominous, so I installed #421.
I did not notice any side-effects (on PHP 8.3). Very large decimals fail in the DB call, but the overall node-save did not proceed and blocked, so that was OK for me right now.
And look: we just now passed the 12 year anniversary of when this issue was opened...
Comment #433
maxilein commented@anybody: I had to read 430 also twice: question for clarification:
This
is the merge into D11.3 dev.
And the merge is green. So ok.
Can you confirm for everybody, please: THIS IS THE ENTIRE PATCH?
Comment #434
xjmLet's hide approaches that we know are definitely not committable because of potential data integrity problems. ;)
Comment #436
xjmAs far as I can see, MR 8282 does apply fine to
main. We need the issue resolved inmainbefore we can backport fixes to other branches, so let's please not confuse the issue with workarounds for older versions of Drupal. Instead, let's focus on review and testing of the current MR.Comment #437
xjmGave it an initial quick look with a couple points that can be addressed.