Floating Point Number parsing: Is there a Catch All algorithm? - c#

One of the fun parts of multi-cultural programming is number formats.
Americans use 10,000.50
Germans use 10.000,50
French use 10 000,50
My first approach would be to take the string, parse it backwards until I encounter a separator and use this as my decimal separator. There is an obvious flaw with that: 10.000 would be interpreted as 10.
Another approach: if the string contains 2 different non-numeric characters, use the last one as the decimal separator and discard the others. If I only have one, check if it occurs more than once and discards it if it does. If it only appears once, check if it has 3 digits after it. If yes, discard it, otherwise, use it as decimal separator.
The obvious "best solution" would be to detect the User's culture or Browser, but that does not work if you have a Frenchman using an en-US Windows/Browser.
Does the .net Framework contain some mythical black magic floating point parser that is better than Double.(Try)Parse() in trying to auto-detect the number format?

I think the best you can do in this case is to take their input and then show them what you think they meant. If they disagree, show them the format you're expecting and get them to enter it again.

I don't know the ASP.NET side of the problem but .NET has a pretty powerful class: System.Globalization.CultureInfo. You can use the following code to parse a string containing a double value:
double d = double.Parse("100.20", CultureInfo.CurrentCulture);
// -- OR --
double d = double.Parse("100.20", CultureInfo.CurrentUICulture);
If ASP.NET somehow (i.e. using HTTP Request headers) passes current user's CultureInfo to either CultureInfo.CurrentCulture or CultureInfo.CurrentUICulture, these will work fine.

You can't please everyone. If I enter ten as 10.000, and someone enters ten thousand as 10.000, you cannot handle that without some knowledge of the culture of the input. Detect the culture somehow (browser, system setting - what is the use case? ASP? Internal app, or open to the world?), or provide an example of the expected formatting, and use the most lenient parser you can. Probably something like:
double d = Double.Parse("5,000.00", NumberStyles.Any, CultureInfo.InvariantCulture);

The difference between 12.345 in French and English is a factor of 1000. If you supply an expected range where max < 1000*min, you can easily guess.
Take for example the height of a person (including babies and children) in mm.
By using a range of 200-3000, an input of 1.800 or 1,800 can unambiguously be interpreted as 1 meter and 80 centimeters, whereas an input of 912.300 or 912,300 can unambiguously be interpreted as 91 centimeters and 2.3 millimeters.

Related

Accept double type only in a given format as method parameter in C#

Is there any possibility to force a parameter tpye to accept only a given format and not only in runtime?
What I mean, for instance I have a method:
public void AcceptTest(double version)
{
}
This method will accept 1.0 but 1.00, 1.0067 and so on.
How can I solve to accept only x.y and nothing else but without check it from code by String.Format or something else.
So I don't even give the possibility to write an unacceptable format in the code editor also.
Thank you!
Your goal is not quite clear, and depending on it any number of answer are possible. Including several that read "you are on a poor track" and "that is impossible".
You can not with Double. Double is defined as a number with a high possibility for decimal places. Every number from it's range is viable, both at compile and runtime.
You could round it automatically, wich might be considered poor behavior.
You could check if the "input" is the same as the "input rounded to the 1st decimal digit" (to see if the other party did the rounding for your).
Note that float imprecision will still result in ending up with wierd decimal places. This is a inherent part of floating point numbers:
https://www.youtube.com/watch?v=PZRI1IfStY0
Some implementations of decimal allow you to specify how many digits after comma to allow. But the .NET one is not among them. At least it avoids the decimal imprecision by lowering the value range.
You could just store as (unsigned) integer. Setting the comma during output would become a display side thing. Maybe make your own structure for this so you can provide your own ToString().
Hop that helps, but calrification would be nessesary. In particular your goal and intention. or the specific problem that makes such a limitation nessesary.

Why does formatting a Double using the "G" standard format string not return the full string?

I hope I have researched this enough that my premise is not totally off base. If so, then the mathematicians out there can set me straight.
My premise is that a Double value such as 12.5 should be rounded to 5 significant figures (NOT decimal places) as 12.500. Instead, using the following C# code, I get 12.5:
Double d = 12.5;
Console.WriteLine(d.ToString("G5"));
I came across this post from 2007 which seems to echo my problem. In fact, I am using those example numbers just to keep things consistent.
My goal here is to better understand the following:
Is my understanding of sig figs mathematically correct? I.e., is my expectation reasonable, or is the output "12.5" somehow correct?
Is this really a (very long-lived) bug in the framework? If so, can/will it be fixed?
Assuming it is a bug, what might I do about it now? Write a hack to determine how many
sig figs you actually got back and then pad it? Roll my own code to
do what the "G" format string was supposed to do? I have come across examples of this on SO already, so perhaps that is evidence that a clean option does not exist.
Additionally, I do realize that the storage issues with Double might negatively impact the rounding aspect of this problem, but for now, I am only concerned with the issue of more sig figs than original digits.
EDIT: I have tested this up to framework 4.5.
See this link on G-Format Specifier. It clearly states:
The result contains a decimal point if required, and trailing zeros after the decimal point are omitted.
A Double value is rounded to 15 significant figures, not five.
Reference: The General ("G") format specifier
Rounding a number to any number of significant figures doesn't mean that the formatted string has to contain that number of digits. If the value is rounded to 12.5000000000000 then it will be formatted into "12.5" because that is the most compact way to represent the value.

Convert string to decimal ignore point

today iv notice that all the place that im using decimal first thing is in the grid show comma instead of point
the second problame is when i edit or insert number with point it ignore the point
like i enterd "2.5" i got it as 25
thats like example code:
decimal a = Convert.ToDecimal("2.5");
the resoult of a is 25.
this problame is happening on the iis and also in my local machine.
it all over the place , how can i fix it ?
What culture are you currently on? This should do the work:
decimal a = Convert.ToDecimal("2.5", new CultureInfo("en-US"));
Also, for the comma showing:
string s = a.ToString(new CultureInfo("en-US"));
You can probably fix it by changing your default culture, or by forcing the use of a specific culture in the conversion calls (a).
Our wonderful, yet somehow slightly disturbing European cousins have gotten the decimal point and comma mixed up at some point in the past, so that thirty-five-thousand-and-seven-point-two-five is written as:
35.007,25
So what's almost certainly happening is that your culture settings are throwing away the thousands separator (regardless of where they are) so that "2.5" is 25. This means that a number like two-and-half-thousand (2,500) would almost certainly become two-and-a-half.
(a): In fact, you probably shouldn't force a specific culture in the conversion call, since the whole point of cultures is to adapt to people's needs. The Europeans thinks little enough of US bods already without giving them more reasons :-) What you're seeing is exactly what's supposed to be happening.
If you need a specific culture, configure your machine that way, and let your code use the default.

how to change culture info on the string or double

I want to change CultureInfo of double or the string.
For example I get double value from the code in format like 3015.0
I don't know in what unit is this but I need value in the meters and these are not in the meters because I am on the altitude of cca.100m
I have tried: double.Parse(test, new System.Globalization.CultureInfo("hr-HR"));
and double.Parse(test, new System.Globalization.CultureInfo());
but nothing is a right format what I need.
Any idea what I can do? This is windows form C# application if this is important. Framework 4.0
EDIT:
As you can see on this LINK I had a similar problem before and it was solved with culture info. Problem is that on picture 1 are the values that I get and on the picture 2 are the real values that I need to get ( when I say real I mean in the right format) I think that problem is in the culture somewhere as on my previously question I had problem with decimal values).
This is not related to Culture info.
Looks like you are getting a measurement in feet while you are expecting it to be in meters. In fact, 100 meters = 328.08399 feet and your measurements might be in 10 feets i.e 3015.0 = 301.5 feet (some GPS receivers do not support floats or doubles and therefore return only integers multiplied by 10 to have one decimal accuracy)
If you are using a cheap GPS receiver than this is expected as the accuracy is not that great (this would explain why you are getting 3015.0 instead of 3280)
I hope this helps.
Your problem has nothing to do with CultureInfo but with unit conversion. Probably you will have to do a unit convertion. Are you sure that the number is not 301.5? This would probably mean that the altitue is given in feet.
double altitudeMeters = 0.3048 * altitudeFeet;
The setting of the current culture will not convert units for you. It only affects the formatting of numbers (for example, some cultures use a comma instead of a period for the decimal point). You'll have to do the units conversion yourself.
double.Parse will simply convert the string into a number. It doesn't do unit conversions. The different culture information is for when there is a decimal comma (e.g. French) etc.
You will have to build some logic into your application to convert the number from what looks like feet to meters. If you can be sure that the data is always going to be in the "wrong" format then a simple feet to meters (1 foot = 0.3048 meters) conversion will work. Given that this is a GPS device you might be able to assume this.
If the numbers can be in any format then you will need to analyse the number and if it's outside the sensible range convert it. However, this will fail if someone enters "100". Is this metres or feet?
To ensure you get the right units you will either have to have the user select the units on a separate control or include the units in the input string. If you do the latter then you'll need to get into parsing the string to see if it contains a units string, stripping it off, parsing the number and the string and then doing the conversion.
Altitude comes from $GPGGA string which indicates the units being used. What does the $GPGGA string look like?
see http://aprs.gids.nl/nmea/#gga
if you look at the raw data in the string you will know if you are collecting the right numbers and their units
Alex

Get the decimal part of a number and the number of places after the decimal point (C#)

Does anyone know of an elegant way to get the decimal part of a number only? In particular I am looking to get the exact number of places after the decimal point so that the number can be formatted appropriately. I was wondering if there is away to do this without any kind of string extraction using the culture specific decimal separator....
For example
98.0 would be formatted as 98
98.20 would be formatted as 98.2
98.2765 would be formatted as 98.2765 etc.
It it's only for formatting purposes, just calling ToString will do the trick, I guess?
double d = (double)5 / 4;
Console.WriteLine(d.ToString()); // prints 1.75
d = (double)7 / 2;
Console.WriteLine(d.ToString()); // prints 3.5
d = 7;
Console.WriteLine(d.ToString()); // prints 7
That will, of course, format the number according to the current culture (meaning that the decimal sign, thousand separators and such will vary).
Update
As Clement H points out in the comments; if we are dealing with great numbers, at some point d.ToString() will return a string with scientific formatting instead (such as "1E+16" instead of "10000000000000000"). One way to overcome this probem, and force the full number to be printed, is to use d.ToString("0.#"), which will also result in the same output for lower numbers as the code sample above produces.
You can get all of the relevant information from the Decimal.GetBits method assuming you really mean System.Decimal. (If you're talking about decimal formatting of a float/double, please clarify the question.)
Basically GetBits will return you 4 integers in an array.
You can use the scaling factor (the fourth integer, after masking out the sign) to indicate the number of decimal places, but you should be aware that it's not necessarily the number of significant decimal places. In particular, the decimal representations of 1 and 1.0 are different (the former is 1/1, the latter is 10/10).
Unfortunately, manipulating the 96 bit integer is going to require some fiddly arithmetic unless you can use .NET 4.0 and BigInteger.
To be honest, you'll get a simpler solution by using the built in formatting with CultureInfo.InvariantCulture and then finding everything to the right of "."
Just to expand on the point about getbits, this expression gets the scaling factor from a decimal called foo:
(decimal.GetBits(foo)[3] & 16711680)>>16
You could use the Int() function to get the whole number component, then subtract from the original.

Categories