Part of the sweep tracked in #518. Finding F21.
Summary
render/locale_format.hpp:71-74 strips every occurrence of groupSeparator unconditionally, before the decimal check and with no check that groups are three digits or that the separator appears before the decimal point.
Verification status
Reproduced. Revision: master @ 4017228d.
normalize("1.5", dec=",", grp=".") = "15" <-- de-DE form, US-style decimal typed
normalize("1.50", dec=",", grp=".") = "150"
normalize("1.2.3.4", dec=",", grp=".") = "1234"
normalize("1,5", dec=".", grp=",") = "15" <-- en-US form, EU-style decimal typed
normalize("1.5", dec=".", grp=".") = "15" <-- separators equal, no diagnostic
normalize("-5" with U+2212 MINUS) = NULLOPT
Not verified: I did not check whether the QML mirror in src/qt/forms/qml/DynamicForm.qml behaves identically. If it does, the two edges are consistently wrong rather than inconsistently, which is a smaller problem but still a wrong value.
Why this is a defect and not a design choice
A German user typing 1.5 into a price field submits 15. This is the single most common real-world locale entry mistake, and the function converts it into a valid-looking value rather than rejecting it — so nothing downstream can catch it. The user is charged ten times with no diagnostic anywhere.
Suggested fix
Validate group placement before stripping: a group separator must be preceded by 1-3 digits, followed by exactly three digits, and must not appear after the decimal separator. "1.5" then correctly returns std::nullopt and the caller can tell the user to fix the entry.
Two smaller fixes while there:
assert(decimalSeparator != groupSeparator) — today the decimal is silently eaten.
- A
negativeSign parameter, so a locale using U+2212 MINUS can round-trip.
What would change the verdict
- Close it if the function is documented as accepting only already-canonical input — but its whole purpose is to normalise user-typed text at the control edge.
tests/test_render_locale_format.cpp has 18 cases and none of them is normalize("1.5", ",", "."). Add it, plus the equal-separators case.
Part of the sweep tracked in #518. Finding F21.
Summary
render/locale_format.hpp:71-74strips every occurrence ofgroupSeparatorunconditionally, before the decimal check and with no check that groups are three digits or that the separator appears before the decimal point.Verification status
Reproduced. Revision:
master@4017228d.Not verified: I did not check whether the QML mirror in
src/qt/forms/qml/DynamicForm.qmlbehaves identically. If it does, the two edges are consistently wrong rather than inconsistently, which is a smaller problem but still a wrong value.Why this is a defect and not a design choice
A German user typing
1.5into a price field submits 15. This is the single most common real-world locale entry mistake, and the function converts it into a valid-looking value rather than rejecting it — so nothing downstream can catch it. The user is charged ten times with no diagnostic anywhere.Suggested fix
Validate group placement before stripping: a group separator must be preceded by 1-3 digits, followed by exactly three digits, and must not appear after the decimal separator.
"1.5"then correctly returnsstd::nulloptand the caller can tell the user to fix the entry.Two smaller fixes while there:
assert(decimalSeparator != groupSeparator)— today the decimal is silently eaten.negativeSignparameter, so a locale using U+2212 MINUS can round-trip.What would change the verdict
tests/test_render_locale_format.cpphas 18 cases and none of them isnormalize("1.5", ",", "."). Add it, plus the equal-separators case.