Regex on decimal numbers with .0 e.g. 38.0

1. What is the issue? Please be detailed.
Using regex to restrict a decimal number to precision of one doesn't work when the user uses 0 as the decimal number.

2. What steps can we take to reproduce this issue?
Create a decimal element in a XFORM with the constraint
((.>32 and .<45) and regex(.,'[0-9]+[.][0-9]')) or .=''

3. What have you tried to fix the issue?
tried:
((.>32 and .<45) and regex(.,'^\d+.[0-9]$')) or .=''
Also tried:
((.>32 and .<45) and regex(.,'^\d+.\d$')) or .=''

4. Upload any forms or screenshots you can share publicly below.

It works well for any number apart from 0, 38.1 through 38.9 but then fails at .0.

Welcome @magp18,
Which number/decimal exactly do you want to exclude around 0? As you use a mathematical and clause (together with the regex), numbers are only valid between 32.1 and 44.9.

Thanks @wroos
Yes it's numbers like 38.0, 37.0, 36.0

Are you using Collect or Enketo? There are differences concerning regex in Enketo!

I tried both, ODK collect was where the issue was first spotted, then I tried uploading my form to the Web application and I got the same behaviour

Is the field/question mandatory?

Yes, the field is mandatory

Valid should be e.g.: 32.1, 33.0, 38.0, 44.0, 44.9 ?
Invalid e.g.: 33, 33., 33.., 33.00, -33.9, 44.90, 44.99, 44, 31.9, 45, 45.0?

The problem is the internal casting (to string): 33.0 and 33. are casted to 33, so the regex fails.

Yes, exactly that corresponds well with the range of numbers I want the regex to let through.

Here are three solutions based on type text and examples that the string cast is the problem with type decimal. RegEx07.xlsx (20.2 KB)
Developed/tested using the ODK Online validator.
You may set required true (or yes).

See also Kobo Help Center article (all based on type text): https://support.kobotoolbox.org/restrict_responses.html

It seems that the system treats decimal 32.0 and 32.00 as (integer) 32. Is this what we attend? Mathematically, yes, but probably not as its string representation?

You can add a calculate or decimal type variable to copy/store the ##.0 entry as real decimal. But there is no solution to keep the .0 digit, as you will still have the cast problem. Even if you enter 32.0 directly in a decimal field, after save you will see 32, without decimal digit. (32 and 32.0 are equal as decimal.)

If we find a way to get the decimal digits as number, a solution with type decimal could be possible. Maybe, @Xiphware or @yanokwa or the community has an idea, how to use type decimal and check for exactly one decimal digit? Unfortunately, the format-number() function is not available so far (https://www.w3.org/TR/xpath-functions-30/#func-format-number).

For the string conversion, see also https://www.w3.org/TR/1999/REC-xpath-19991116/#function-string

A number is converted to a string as follows ...

  • if the number is an integer, the number is represented in decimal form as a Number with no decimal point and no leading zeros, preceded by a minus sign (-) if the number is negative
  • otherwise, the number is represented in decimal form as a Number including a decimal point with at least one digit before the decimal point and at least one digit after the decimal point, preceded by a minus sign (-) if the number is negative; there must be no leading zeros before the decimal point apart possibly from the one required digit immediately before the decimal point; beyond the one required digit after the decimal point there must be as many, but only as many, more digits as are needed to uniquely distinguish the number from all other IEEE 754 numeric values.

For decimal, see also https://www.w3.org/TR/xmlschema-2/#decimal.

If you could accept also UI entries like 33 or 33. (without decimal digits) or 33.00, you could use type decimal and a regex: (see example xlsx above)

To be clear, your requirement stipulates that '33' must be considered invalid, but '33.0' is valid. Correct?

Or is '33' also valid?

Hi @Xiphware,
The more complicated case seems: 33.0 is valid, but 33 or 33. or 33.00 are invalid. And you want to use a decimal type UI (or at least to store a decimal copy) with exactly one decimal digit, permanently. :partying_face: format-number() would get the job, but seems not available yet.

yes, that's correct. 33 should not be valid in that case.

Hmmm I see your point but should we treat 33.0 as 33 ? For mathematic calculations as you said, it does make sense since it will be seen as the same number, but for capture of data is a little bit misleading since 33.0 is more precise than 33 :thinking:

These are essentially all the same numbers (to XPath), so if you do need to distinguish between them then you are not going to be able to capture them using a decimal question, but must instead use a text type question, and treat them as char strings in your validation logic. That doesn't preclude you converting the string - either explicitly or implicitly - to a number to, say, perform < or > comparisons.

Note, at the end of the day, everything gets converted to a suitable string representation in the XML form payload that is sent up to your server. If you send up a decimal question response then the underlying XForm engine (Collect, Enketo, ...) is going to convert your (decimal) number to a string for this XML payload, and you dont have a lot of control over how (eg it could well drop a 'xxx.0' entirely...). If you want strict control over how your (decimal) numbers are represented, or validated, then you'll really need to capture and treat them as text (strings)

1 Like