I'm going through some code from another developer and I found some code that seems... well ugly and unnecessary.
I understand that when parsing strings, we need to use cultural info properly when otherwise you can get into the "1.2" vs "1,2" type issues. In this case, the developer is using a NumericUpDown which is stored as a decimal.
float fValue = System.Convert.ToSingle(numericUpDown1.Value, System.Globalization.CultureInfo.InvariantCulture)
My questions are:
- is the following code equivalent?
- Will cultural info be handled properly?
- Can this cast throw an exception?
float fValue = (float)numericUpDown1.Value;
If they are not equivalent (or safe) can you please explain why and propose a "correct" solution?
Thanks in advance!
Convert.ToSingle(value, culture) calls ((IConvertible)value).ToSingle(culture). decimal's IConvertible.ToSingle then ignores the passed-in culture and returns Convert.ToSingle(value), which just returns (float)value.
Yes, your code is equivalent.
Yes, your code is equivalent to that only if the numericupdown1 value is small.
but if that value is 41.00027357629127 or something like this it will return 4.10002732E+15.
so,by using float.Parse("numericUpDown1.Value", CultureInfo.InvariantCulture.NumberFormat); you can get the exact value
Related
I have some generic database routines. One that I make use of quite frequently is based on code similar to this code, but for an OleDbDataReader, not the generic IDataReader from that code.
I was playing around with this, and decided to test what would if I tried to (say) retrieve a value from a field, where I had input the incorrect type. Eg: I try to get a double value from a database column that is actually an integer:
reader.GetValue<double>("Column_that_is_Integer_Type");
...and, unexpectedly, it seamlessly converts the database column integer value to a double. Hmm. OK - I get a usable value back, but what about other conversions?
reader.GetValue<bool>("Column_that_is_Integer_Type");
This returns true. Not exactly what I want, and I get no error.
reader.GetValue<DateTime>("Column_that_is_Integer_Type");
This one at least throws an InvalidCast Exception.
Because of all this, I added the follow type checks to the code:
if (theReader.GetFieldType(fieldIndex) == typeof(T))
{
//Carry on...
}
else
{
//Raise an error
}
I think this is the safest way of preventing issues, but was wondering if there is a somewhat 'generic' compatibility check that can be performed? My onward use of a given variable probably won't care whether '39.5' retrieved from a database that stores it as a double is passed around as a decimal, but it will certainly care if it is being passed around as a bool.
My default position will be to throw an error if someone gets the column data type wrong, but I was interested enough to ask the question: Is there a robust method for checking whether type conversions preserve appropriate data integrity?
Eg. Integer type converted to Double: OK. Double converted to Integer: Nope.
Double type converted to Decimal: OK. Double converted to DateTime: Nope.
I think I've answered my own question, but interested in opinions.
So I'm trying to use the RangeValidator to ensure the minimum and maximum value of Int64 is validated. But, the only or closest option that I seem to have is Double.
Dim Range As New RangeValidator
Range.ControlToValidate = "..."
Range.Type = ValidationDataType.Double
Range.MinValue = Int64.MinValue.ToString
Range.MaxValue = Int64.MaxValue.ToString
I don't want to use a custom validator. I suppose I could use some crazy regular expression.. but just trying to understand why I can't do this.
For example, the max value of Int64 is:
9223372036854775807
But this validates:
9223372036854775808
9223372036854775810
9223372036854775899
It will not validate if I jump up a larger number:
9223372036854799999
I'm assuming it's due to some conversion taking place for a Double.
I do see there's a LongValidator in System.Configuration, but I'm trying to avoid creating a CustomValidator type if at all possible and it's a different validate type, as in not intended for use like RangeValidator.
All of this is more so for learning purposes than anything else. I'm aware I can jump through other hoops but hoping to get better clarity.
Tagged C# as well. Ignore my vb.net example.. not really important based on the question. Will convert any code either way.
I found a plausible answer on a forum by PLBlum
I want to clarify something. RangeValidator does not use a
regularexpression to validate a double. It uses a regular expression
to extract the digits and decimal character out of the string. It then
converts the result into an actual number using the javascript
parseFloat() function. If that function cannot convert, the
RangeValidator knows the value was not acceptable to javascript.
I suspect that the problem is that the value you are assigning to
MaximumValue is too large for javascript's floating point. I think
both .net's Double and javascript are 80bit floating point but its
possible they are slightly different at the min and max range. In any
case, I recommend changing your code like this:
Use a CompareValidator, Operator=DataTypeCheck, Type=Double. It will report an illegal value due to the format or its too big for an 80 bit
number.
Use another CompareValidator, Operator=GreaterThanEqual, Type=Double, ValueToCompare= your minimum double. This will report an
error if anything is below the minimum. The max was handled in the
other validator.
Maybe that explains why it is validating incorrectly.
Unless someone else wants to chime in, realistically there's one way I found to do this, and that's with a custom validator. There's a function in System.configuration that allows the ability to check if a value is a valid "Long". The problem is, if you want client-side validation it seems the only valid option is to use a regex. Otherwise, the number is just too big and JavaScript parses it as a float, as Sam mentioned.
So, I ended up using a regex as to gain client-side and server-side validation.
I was posting similar post already. And I did get an answer in theory but I really need somone to help me.
So I am using this EXAMPLE for testing my GPS. What I want to do is how to get standard decimal values like 56,322415 for latitude and longitude.
Because now I am getting 5304,254 value.
I need it in this form because I will use this for Google maps.
Can you check code and tell me what should I left out or how to convert it to WGS84 format.
Thank you.
EDIT:
Here is the picture that is explaining what I see when I run the program
And this is the one that is one the codeproject page:
EDIT 2:
So when I use THIS CONVERTER to see what decimal values I need to get when I have degrees I don't get the same values.
This is the value that I should get :
And this is the value that I get in the program:
46.64662666666667
Any idea why I can't get same conversion?
the following line in the ParseNMEA function is the one that does the conversion from WGS84 to the British Ordinance Survey coordinates:
return Transform(deciLat, deciLon, height);
so if you remove that line and just use deciLat and deciLon values, you should be fine...
Addition after question update:
The code you're referring to does not take local number formats into account, so if this goes wrong, it might be because of erroneous decimal symbol matching.
You can use the following functions to do the conversion, presuming the input is always in the invariant culture (with decimal points):
string[] arrLon = strLon.Split(new char[]{'°', '"'}, StringSplitOptions.RemoveEmptyEntries);
double dblLon = double.Parse(arrLon[0]) + double.Parse(arrLon[1], new System.Globalization.NumberFormatInfo()) / 60;
double deciLon = arrLon[2] == "E" ? dblLon : - dblLon;
Additional info:
NumberFormatInfo: http://msdn.microsoft.com/en-us/library/system.globalization.numberformatinfo.aspx
It sounds like your problems are now solved, I do know that there were issues caused by different local number formatting which were raised and resolved in one of the comments:
http://www.codeproject.com/Articles/13577/GPS-Deriving-British-Ordnance-Survey-Grid-Referenc?msg=2106791#xx2106791xx
If you simply want decimal values for lat and lon, I guess you can throw the entire thing away now and just use the solution provided by Dirk! =o)
Alex
well in my database i had a colum for price of one product
i had it as float, my problem is if i saved it since my c# application
as 10.50 .. in a query it returns 10,50 and if i update i get a error
10,50 cant convert to float ... or something so..
and if i saved it as decimal, in queries inside sql management .. are ok..
but in my c# application... i get the same error..
10.50 retuns as 10,50 i dont know why, and how to solved it.. my unique solution is saved it
as varchar...
That's a localisation problem of some sort. 10,50 is the "European" way of writing ten and a half. If you're getting that from your select statements then your database is probably configured incorrectly.
Generally speaking you should use the same type throughout your layers. So if the underlying types in the database are x, you should pass around those data with identical types in c#, too.
What type you choose depends on what you are storing--you shouldn't be switching around types just to get something to "work". To that end, storing numeric data in a non-numeric type (e.g. varchar) will come back to bite you very soon. It's good you've opened this question to fix that!
As others have miraculously inferred, you are likely running into a localization issue. This is a great example of why storing numbers as strings is a problem. If you properly accept user input in whatever culture/localization they want (or you want), and get it into a numeric-type variable, then the rest (talking to the DB) should be easy. More so, you should not do number formatting in the database if you can help it--that stuff is much better placed at the front end, closer to the users.
I think your setting in windows regional and language for decimal symbol is wrong.please set it to dot and again test it.
This may help out for temporary use but I wouldn't recommend it for permanent use:
Try making it so that just before you save the file, convert the number to a string, replace the commas with periods (From , to .) and then save it into the database as the string, hopefully it should see that it is in the correct format and turn it into what the database sees as "Decimal" or "Floating".
Hope this helps.
Yep, localization.
That said, I think your pice is being stored on a "money" field in SQLServer (I'm assuming it's SQLServer you're using). If that was a float in the DB, it would return it with a normal decimal point, and not the European money separator ",".
To fix:
Fist DO NO USE FLOAT in your c# code, unless you absolutely require a floating point number. Use the decimal type instead. That's not just in this case, but in all cases. Floating point numbers are binary (base-2), not decimal (base-10), so what you see in the interface is only a decimal approximation of the actual number. The result is that frequently (1 == 1) evaluates as false!
I've run into that problem myself, and it's maddening if you don't know that can happen. Always use decimal instead of float in c#.
Ok, after you've fixed that, then do this to get the right localization:
using System.Globalization;
...
NumberFormatInfo ni = new NumberFormatInfo();
ni.CurrencyDecimalSeparator = ",";
decimal price = decimal.Parse(dbPriceDataField, ni);
Note that "dbPriceDataField" must be a string, so you may have to do a ".ToString()" on that db resultset's field.
If you end up having to handle other "money" aspects of that money field, like currency symbols, check out: http://msdn.microsoft.com/en-us/library/system.globalization.numberformatinfo.aspx
If you need more robust error handling, either put that decimal.Parse in a try/catch, or use decimal.TryParse.
EDIT --
If you know what culture (really, country), the db is set to, you can do this instead:
using System.Globalization;
...
CultureInfo ci = new CultureInfo("fr-FR"); // fr-FR being "french France"
decimal price = decimal.Parse(dbprice, ci.NumberFormat);
Such problems were faced by me in my Web Apps... but i found the solution like I was fetching my price value in textbox. So I was have database attached with that. So when you attached your database with textbox... When you right click textbox and click Edit DataBinding.... in that you have to provide.... type like in Bind Property..... {0:N2}
This will work only for web apps or websites... not for desktop applications...
It seems like there would be a ton of uses for a fixed point data type. Why is there not one in .NET?
Note: I understand we can create our own classes/structs to suit our fixed point purposes and needs. That's not my question. I want to know WHY MS decided not to include a fixed point numeric data type.
You're looking for the little-known System.Data.SqlTypes.SqlDecimal class.
Decimal (base-10 floating point) was deemed to be good enough.
One problem probably has to do with the question: where do you fix the point? A type in .NET cannot be parametrized by other arguments than types, so FixedNum<18,6> is simply not possible. And you do not want to create FixedNum1x0, FixedNum1x1, FixedNum2x0, FixedNum2x1, FixedNum2x2, etc.
You need to be able to parametrize your fixed point type, not just values, because that would lead to nigh impossible to track mistakes:
FixedNum f() { return new FixedNum(1, decimals: 2); }
FixedNum x = new FixedNum(1, decimals: 0);
...
x = f(); // precision of x increased.
So you'd need to check and constrain your fixed point values every time you get them from something that's not a local variable. As you do with decimal when you want a fixed scale or precision.
In other words, given the limitations of the .NET type system, decimal is already built-in implementation of the FixedNum class above.