Number <--> String

Hi

We have some troubles with locale settings and numbers. I did try to describe all the different behaviours and come to the following statements:

  • The Default Locale in Preferences defines - amongst others - the decimal separator character. E.g. Deutsch (Deutschland) uses a comma, Deutsch (Schweiz) uses a point.

  • The Number Format in Preferences only defines, where the separators are (thousands separator and decimal separator) and how many decimal numbers should be displayed. It does not define, which separator character to use.

  • utils.numberFormat(number, format) creates a string following the format rules using the separator character defined by Default Locale.

  • utils.stringToNumber(string) only interprets the point as decimal separator character. It ignores the Default Locale.

  • A formatted number field in Servoy (#,###.00) does not allow the same characters as input values for different Default Locales. E.g. for Deutsch (Deutschland) digits, comma and point is allowed. For Deutsch (Schweiz) digits and points are allowed.

  • Rounding is wrong for even values followed by a 5. E.g. 1.345 formatted with “#,###.00” is rounded to 1.34.

My conclusions: As a programmer I have to be careful when using stringToNumber. I have to guarantee, that the decimal separator is a point. And I should not base on locale settings. Point f. is a bug and should be fixed.

Can anybody confirm my points and my conclusions? Is the behaviour known and expected? Documented?

Thanks for feedback and more clarification. I still feel, that I don’t have an clear view.

I’d say point f is not a bug, but expected behaviour. There are different rounding strategies that could be used. The DecimalFormat() used here uses HALF_EVEN as strategy. See here:

http://download.oracle.com/javase/6/doc … #HALF_EVEN

In any case, this is not something that Servoy does, but it’s plain Java. If you want something rounded the way you want it, you should round before you format.

Thank you, Patrick, for your answer. I had never heard about this strategie :?

I followed your link and would like to use HALF_UP as rounding strategy. I’m sure you can help me, you seem to know a lot about it: Where is this defined and how can it be changed?

Thanks and regards
Birgit

PS: Were you aware of stringToNumber not using the locale settings? Did you expect it?

Internally (I guess!) Servoy uses a java.text.DecimalFormat instance to format your number. There are many ways how you can round a number, the default for the DecimalFormat instance is the half even that I sent you the link for. That “strategy” can be changed in Java 6 or later, but that possibility is not accessible from within Servoy. So you cannot modify that.

As I said before, that rounding is not wrong, it is just not the rounding you want here. If rounding is an issue for you, you need to round the number you want to display first to your liking (in a calculation for example) and then display the result with the format applied. In our solutions, we always round ourselves when that is important (for example a VAT calculation, that can easily lead to 1.345676 Euros).

Regarding the stringToNumber function: That method doesn’t do anything with locales. It basically removes all non-digit characters from a given String, respecting a dot (.) as a decimal point. So it is really a very simple helper method to create a number. For example:

var vMyString = "abvv12asd,567xyz";
var vMyNumber = utils.stringToNumber(vMyString);

leads to vMyNumber: 12567

where

vMyString = "abvv12asd.567xyz";

leads to vMyNumber: 12.567 (which in German German is 12,567).

Looking at Servoy source code you can see why that happens:

cc.equals(".")

There is an explicit check for a dot. A String is created that only contains digits and a dot and that is parsed to a double.

So yes, I expected that. What kind of input are you having when you use stringToNumber()?

birgit:

  1. The Number Format in Preferences only defines, where the separators are (thousands separator and decimal separator) and how many decimal numbers should be displayed. It does not define, which separator character to use.

that something the default locale of the user defines. thats not something for the developer to set.

birgit:

  1. utils.numberFormat(number, format) creates a string following the format rules using the separator character defined by Default Locale.

as it should be.

birgit:

  1. utils.stringToNumber(string) only interprets the point as decimal separator character. It ignores the Default Locale.

this is a bug, please make a case.

birgit:

  1. A formatted number field in Servoy (#,###.00) does not allow the same characters as input values for different Default Locales. E.g. for Deutsch (Deutschland) digits, comma and point is allowed. For Deutsch (Schweiz) digits and points are allowed.

thats is guess logical because as you say the . has different meanings.

birgit:

  1. Rounding is wrong for even values followed by a 5. E.g. 1.345 formatted with “#,###.00” is rounded to 1.34.

servoy it self uses rounding half up
But maybe the method you call uses a default decimal behavior. Maybe we could make this in sync
please make a case for this

jcompagner:
servoy it self uses rounding half up
But maybe the method you call uses a default decimal behavior. Maybe we could make this in sync
please make a case for this

I think she is not using a method, but just the format on a field in a form?

First, thank you both, Johan and Patrick, for helping me.

There were many questions in the former replies:

What happened? The problem occurred two days ago, for all (5) users at one of our customers. If they did enter an amount and left the field, the amount did change. It was multiplied by 100. We were looking at this and found, that the Default Locale was set to “de, DE”. So far we don’t know, how it could have changed for all users. We did let the users change it back to “de, CH” and everything works now as expected.

But: We now want to understand the influence and the consequences of the different settings better than we did so far. This is why I was looking deeper into it. I found the unbalance of possible characters a user can enter in number fields (point and digits for “de, CH” vs. point, comma and digits for de, DE). For CH, the thousand separator (apostrophe) cannot be entered. For DE, it can (point) but the users intention was a decimal sign.

Back to the points:

  1. numberFormat() works fine and as expected for me.

  2. The unbalance of valid chars leeds to problems. Maybe it would be better, if users cannot enter a thousand separator sign? But I’m not sure what the best would be.

  3. stringToNumber() works as explained in detail by Patrick: Ignores every char instead of numbers and a point (first point, last point? :D ). Ok for me but unexpected. I have to go through my code and guarantee that the decimal sign is always a point before converting a string to a number. You say it is a bug, Johan? So, you expected the method to use the Default Locale, too?

  4. Patrick is right, I was only using the format and no method for rounding an number entered in a field. I thought, the rounding is a bug. I did never read about rounding startegies before. I simply expected HALF_UP as default. And this is what you says, too, Johan. Servoy uses HALF_UP. How can my format behave like HALF_EVEN? Could you check at Servoy, Johan?

  5. We still don’t know, where the client settings for Default Locale in the preferences come from (not from the server, right? There, it is only defined for web clients) and when and how it can change. Any software updates? Java updates?

Any answers will be appreciated a lot. Best regards.

birgit:
2) The unbalance of valid chars leeds to problems. Maybe it would be better, if users cannot enter a thousand separator sign? But I’m not sure what the best would be.

i don’t get what really goes wrong here. Do you have an example

birgit:
3) stringToNumber() works as explained in detail by Patrick: Ignores every char instead of numbers and a point (first point, last point? :D ). Ok for me but unexpected. I have to go through my code and guarantee that the decimal sign is always a point before converting a string to a number. You say it is a bug, Johan? So, you expected the method to use the Default Locale, too?

this method should use the default locale to determine what the decimal separator is.
Now it always just assume .
which is wrong.

birgit:
4) Patrick is right, I was only using the format and no method for rounding an number entered in a field. I thought, the rounding is a bug. I did never read about rounding startegies before. I simply expected HALF_UP as default. And this is what you says, too, Johan. Servoy uses HALF_UP. How can my format behave like HALF_EVEN? Could you check at Servoy, Johan?

Servoy uses HALF_UP in fields and ui input.
I guess not in that specific method, there we just use the default java version.
You could create a case so that we also make that HALF_UP

birgit:
5) We still don’t know, where the client settings for Default Locale in the preferences come from (not from the server, right? There, it is only defined for web clients) and when and how it can change. Any software updates? Java updates?

no idea, curious if those systems also had it configured in there local servoy_client properties file.
(but thats a bit late to see now i guess)

jcompagner:

birgit:
2) The unbalance of valid chars leeds to problems. Maybe it would be better, if users cannot enter a thousand separator sign? But I’m not sure what the best would be.

i don’t get what really goes wrong here. Do you have an example

Example:
locale is “de, de”
User enters 123.45 → when leaving the entry field, the value is changed into 12345.00
Problem: User can enter a point which is ignored.

locale is “de, ch”
User enters 123.45 → when leaving the entry field the value stays at 123.45
No problem: Since the user cannot enter a comma. He/She can only enter a char, which is a valid decimal separator.

jcompagner:

birgit:
3) stringToNumber() works as explained in detail by Patrick: Ignores every char instead of numbers and a point (first point, last point? :D ). Ok for me but unexpected. I have to go through my code and guarantee that the decimal sign is always a point before converting a string to a number. You say it is a bug, Johan? So, you expected the method to use the Default Locale, too?

this method should use the default locale to determine what the decimal separator is.
Now it always just assume .
which is wrong.

Will this be fixed? When?

jcompagner:

birgit:
4) Patrick is right, I was only using the format and no method for rounding an number entered in a field. I thought, the rounding is a bug. I did never read about rounding startegies before. I simply expected HALF_UP as default. And this is what you says, too, Johan. Servoy uses HALF_UP. How can my format behave like HALF_EVEN? Could you check at Servoy, Johan?

Servoy uses HALF_UP in fields and ui input.
I guess not in that specific method, there we just use the default java version.
You could create a case so that we also make that HALF_UP

It is not a method, I use. I only format the field.
Did I get you right, now it’s Java which is doing the rounding? And Java uses HALF_EVEN? And you could overwrite this in Servoy?
Then I’ll make a case, since I prefer HALF_UP as default. Probably it depends on others requests what you’ll implement as default.

Thanks and best regards

birgit:
Example:
locale is “de, de”
User enters 123.45 → when leaving the entry field, the value is changed into 12345.00
Problem: User can enter a point which is ignored.

locale is “de, ch”
User enters 123.45 → when leaving the entry field the value stays at 123.45
No problem: Since the user cannot enter a comma. He/She can only enter a char, which is a valid decimal separator.

But this is how it works, . is not an invalid char. For some locales it is: #.###,## and for others it is #,###.##
Thats just the way it is. And the parser if you do: 123.45 when . is the thousand separator i guess doesn’t understand it and just ignores it.

birgit:
3) stringToNumber() works as explained in detail by Patrick: Ignores every char instead of numbers and a point (first point, last point? :D ). Ok for me but unexpected. I have to go through my code and guarantee that the decimal sign is always a point before converting a string to a number. You say it is a bug, Johan? So, you expected the method to use the Default Locale, too?

Will this be fixed? When?

please make a case then we can look at it.

birgit:
It is not a method, I use. I only format the field.
Did I get you right, now it’s Java which is doing the rounding? And Java uses HALF_EVEN? And you could overwrite this in Servoy?
Then I’ll make a case, since I prefer HALF_UP as default. Probably it depends on others requests what you’ll implement as default.

we don’t override it, its not a default that can be set, its just that it needs to be configured and used when parsing.
and utils.numberFormat() uses the default java number formatting, so i guess if we want to have their the same we shouldn’t be using that but using our special number format class that does that
Also make a case for this

jcompagner:

birgit:
Example:
locale is “de, de”
User enters 123.45 → when leaving the entry field, the value is changed into 12345.00
Problem: User can enter a point which is ignored.

locale is “de, ch”
User enters 123.45 → when leaving the entry field the value stays at 123.45
No problem: Since the user cannot enter a comma. He/She can only enter a char, which is a valid decimal separator.

But this is how it works, . is not an invalid char. For some locales it is: #.###,## and for others it is #,###.##
Thats just the way it is. And the parser if you do: 123.45 when . is the thousand separator i guess doesn’t understand it and just ignores it.

I give up. I obviously can’t explain the asymmetry. Talking would be much easier than writing. Maybe I wait for the Servoy Camp and catch you there.

I just checked it, and yes you are right you can’t type a , because in the swiss locale , is not the thousand separator
that is the ’

try it that one you can type…

So there is no asymmetry, its just that the locale defines the thousand separator (in the usa/uk: , germany/holland: . swiss: ’ ) and the decimal separator (in usa: . germany/holland: , swiss: .)

this is all very easy to test:

make a form variable:

var mynumber = 11000.11

place that format variable on the form in a textfield
set the format to: #,###.##

swiss: 11’000.11
germany: 11.000,11
uk: 11,000.11

swiss: I cannot enter ’

weird i can ’ just fine
depending on the keyboard settings i guess you have to type ’ and then a space to get the ’
don’t know if that is the same for your setting.

birgit:
Hi

We have some troubles with locale settings and numbers. I did try to describe all the different behaviours and come to the following statements:

utils.stringToNumber(string) only interprets the point as decimal separator character. It ignores the Default Locale.

This will be fixed in Servoy 6.0.2 .

Great, thank you!
Regards
Birgit