Skip to content

Radix Separators, Thousands Separators, Parsing, and Validation in NumericalEntry #106

Description

@jimorc

Problems

There are a number of problems with the current implementation of the NumericalEntry widget. They are numbered below (inside parentheses):

Currently, the NumericalEntry widget accepts the following:

  • (minus) as the first character only.
  • 0-9 to represent digits. (1)
  • The full-stop (period or dot) and comma characters as the possible radix (decimal) separator and the other as possible thousands separators. (2) Any number of these characters in any position other than in front of the minus sign may be added to the string. (3)

Parsing of the value is left to the user (the programmer) of the widget. The main problem here is that strconv.ParseFloat appears to only support the full-stop character as the radix separator, and the text cannot contain thousands separators. (4)

None of this is documented anywhere. (5)

I propose that we deal with each of these problems as follows:

Problem 1: Digits

Not all languages use the Arabic numerals (0 through 9). Support for non-Arabic numerals would be difficult because we would have to ensure that there is not a mixture of numerals (Arabic, non-Arabic for each language or locale). Note that various International standards only specify Arabic numerals.

Recommendation: Only accept Arabic numerals, so no change. If non-Arabic number input is required, this would probably best be handled by creating a separate widget.

Problem 2: Radix and Thousands Separators

The radix separator is the character used to separate the integer part of a number from its fractional part.
Various languages use either the full stop, interpunct (middle dot), or comma as the radix separator. Other languages, mostly those that accept digits other than Arabic numerals, also accept other language-specific characters as the radix separator.

Recommendation: Accept the full stop, interpunct, and comma as the radix character.

The thousands separator is used to make the reading of large numbers easier. The separator character is typically positioned so as to separate each set of three digits, but this is not universal. In some countries, the thousands separator is used only in the integer portion of the number, while in others, it may also be used in the fractional part of the number. Each language, or even locale, may specify a different thousands separator. The thousands separator is usually one of the following:

  • a full stop if the radix separator is a comma.
  • a comma if the radix separator is a full stop or middle dot.
  • a space, half-space, or thin-space character.
  • an apostrophe.
  • an underscore.
  • a language- or locale-specific character.

The International Bureau of Weights and Measures, as well as a number of other groups, specify that the thousands separator be a thin-space (U+2009), except that no separator should be used for numbers between 1000 and 9999. There is also the non-breaking thin-space (U+202F) character. A number of other standards (e.g. International Union of Pure and Applied Chemistry, the AMA Manual of Style, and the UK Metrication Board) also specify the use of a thin-space.

Unfortunately, each country, or industry, can specify its own standard or recommendation, and there may be countries where the radix and thousands separators are locale-specific. For example, the maritime industry uses the underscore character as the thousands separator. In Canada, the en-CA locale uses the full-stop as the radix separator, the comma is used in monetary values, and either the comma or one of the space characters is used in numbers as the thousands separator. The fr-CA locale uses the comma as the radix separator and a space or thin-space character is usually used as the thousands separator.

__
Recommendation: Accept the space, thin-space, non-breaking thin-space, apostrophe, and underscore characters in addition to the full-stop and comma characters as separators. Other language-specific separators should be handled with a separate widget that also handles non-Arabic numerals.

Problem 3: Any Number of Separator Characters Is Allowed

While the thousands separator is usually used every third digit, this practice is not universal. In some countries or locales, the number of digits may vary. For example, in Korea, India, and a number of other countries, the separator is placed three digits to the left of the radix, but then every 2 digits to the left thereafter.

If the NumericalEntry widget displays integer values, then the first separator character can be assumed to be the thousands separator. Attempting to enter a different separator should not be allowed.

Recommendation: For NumericalEntry widgets that display integer values, accept the first separator as the thousands separator. Do not allow the use of any other separator character thereafter.

We run into problems when the NumericalEntry widget displays a floating-point number. There is no way to tell whether the first separator is intended as the radix separator or the thousands separator. If the same separator is entered again, then it should be treated as the thousands separator.

Recommendation: Add a Radix field to the NumericalEntry widget and a NewNumericalEntryWithRadix function that specifies the radix character. Allow only one radix character in the NumericalEntry value. Accept only the full stop, interpunct, or comma character.

Recommendation: If the Radix field is empty and only one separator is entered, assume that character is the radix. If a different separator character is entered, then it is not possible to tell which character is the radix separator and which is the thousands separator. In that case, the value should be validated as invalid. When more than one of the same separator characters is entered, then it can be assumed that this is the thousands separator, and the value can be validated as appropriate. See the section on Validation below.

Problem 4: Parsing of the Value is Left to the User

As currently implemented, parsing of the text in the widget is left to the user. strconv.ParseInt accepts only digits for the text to parse, or it returns 0 and an error. strconv.ParseFloat accepts only digits and a single full stop. Any other text results in a return of 0 and an error. This does not appear to be locale specific. The user is currently responsible for converting the text to a value that either of these parse functions will accept.

Recommendation: Add ParseInt and ParseFloat methods to NumericalEntry. These methods should first validate the text, then remove any thousands separators, and change any radix character to a full stop before passing the text to the strconv functions. If the validation fails, then 0 and an error should be returned. Otherwise, the result of the call to strconv.ParseInt or strconv.ParseFloat should be returned.

Validation

Entry has a Validator field that accepts a function to be called whenever the value in the Entry is changed. Two levels of validation are required for NumericalEntry widgets:

  1. The text needs to be validated as noted in problem 3.
  2. Additional value specific validation must be performed. For example, if a NumericalEntry widget is used in a spinner, then the value must be validated to be between the minimum and maximum values that are set for the spinner. It is the responsibility of the user to provide that validation.

Recommendation: Provide a validation method that can be called to validate the text for problems specified in Problem 3. This method should not be called automatically, but only from a user provided validation function.

Problem 5: Lack of Documentation

There is currently no documentation except the source code provided for the NumericalEntry widget. That may have been acceptable for the original widget code, but changes since then, and especially the changes recommended above need to be documented.

Recommendation: Create documentation for the NumericalEntry widget, including what characters are accepted and rejected as input. Describe the ParseInt and ParseFloat methods, and how to validate the input. Create a program that shows how it can be used.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions