I've run into an unusual quirk in a program I'm writing, and I was trying to figure out if anyone knew the cause. Note that fixing the issue is easy enough. I just can't figure out why it is happening in the first place.
I have a WinForms program written in VB.NET that is displaying a subset of data. It contains a few labels that show numeric values (the .Text property of the labels are being assigned directly from the Decimal values). These numbers are being returned by a DLL I wrote in C#. The DLL calls a webservice which initially returns the values in question. It returns one as a string, the other as a decimal (I don't have any control over the webservice, I just consume it). The DLL assigns these to properties on an object (both of which are decimals) then returns that object back to the WinForm program that called the DLL. Obviously, there's a lot of other data being consumed from the webservice, but no other operations are happening which could modify these properties.
So, the short version is:
WinForm requests a new Foo from the DLL.
DLL creates object Foo.
DLL calls webservice, which returns SomeOtherFoo.
//Both Foo.Bar1 and Foo.Bar2 are decimals
Foo.Bar1 = decimal.Parse(SomeOtherFoo.Bar1); //SomeOtherFoo.Bar1 is a string equal to "2.9000"
Foo.Bar2 = SomeOtherFoo.Bar2; //SomeOtherFoo.Bar2 is a decimal equal to 2.9D
DLL returns Foo to WinForm.
WinForm.lblMockLabelName1.Text = Foo.Bar1 //Inspecting Foo.Bar1 indicates my value is 2.9D
WinForm.lblMockLabelName2.Text = Foo.Bar2 //Inspecting Foo.Bar2 also indicates I'm 2.9D
So, what's the quirk?
WinForm.lblMockLabelName1.Text displays as "2.9000", whereas WinForm.lblMockLabelname2.Text displays as "2.9".
Now, everything I know about C# and VB indicates that the format of the string which was initially parsed into the decimal should have no bearing on the outcome of a later decimal.ToString() operation called on the same decimal. I would expect that decimal.Parse(someDecimalString).ToString() would return the string without any trailing zeroes. Everything I find online seems to corroborate this (there are countless Stack Overflow questions asking exactly the opposite...how to keep the formatting from the initial parsing).
At the moment, I've just removed the trailing zeroes from the initial string that gets parsed, which has hidden the quirk. However, I'd love to know why it happens in the first place.
It's because the scaling factor also preserves any trailing zeros in a Decimal number. Trailing zeros do not affect the value of a Decimal number in arithmetic or comparison operations. However, trailing zeros might be revealed by the ToString method if an appropriate format string is applied.
Related
Checked all questions in SO that seemed related to my problem but could not find any answer that helps in my scenario.
I am developing an Asp.Net MVC 5 application with razor views and SQL Server as the database. I have a requirement to convert values from English to Metric units and back (Inches to Millimeter and back, Horsepower to Kilowatt and back etc.) on the go while user types in one of the unit value fields.
I achieve it by using UnitsNet conversion functions through ajax calls from UI on jQuery keyup events of the input fields. This works fine when user types in the values, and the converted values appears with several decimal points.
For example, 470 Hp (Horsepower) = 345.6844125 Kw (Kilowatts), and it correctly converts back to Hp.
However, when the values are rounded off, the conversion back to original value gives different result since the conversion happens on the rounded value.
I.e., 470 Hp = 345.68 Kw. And when converting back to Hp, it comes as: 345.68 Kw = 469.99 Hp.
The same happens when saving the values into database also. The column that saves the value is decimal (8,2) which obviously rounds off the values and gives this same results.
I would like to know your opinions on what would be the best way to handle this, to show consistent values on screen while converting and re-converting the values. Keeping all fields non-rounded, and also allowing maximum decimal points in the database minimizes this issue, but I am not sure if it is the correct way of handling this. Any help is appreciated.
I tried using the BigInteger implementation for Unity, but it still overflows(Or maybe I used in a wrong way? Im not sure also, it can only do 20 characters of ints which is like 64 bits).... This is how my thing works, I have a Hexadecimal which is has 64 characters, and then for me to do arthmetic computations, I want to convert it first into a decimal form and store it in variable.
public BigInteger x = 0;
and then here where it overflows... HexToDecimal is a function that takes a string of Hexadecimal and returns the decimal form of that.
x = HexToDecimal(hex);
a sample output of HexToDecimal is
105627842363267744400190144423808258002852957479547731009248450467191077417570
that's the ideal size of a number I want to store.
It works if I used very small numbers like hundreds thousands or something. but BitInteger kinda limits it to 20 characters only cause I tried declaring a variable like this, just to know the where BitInteger limits me
public BigInteger x = 10000000000000000000
when I add another "0" there, its throws an error stating that integral is too large
The way you've instantiated your BigInteger is a convenience method for smaller numbers (see "instantiating a BigInteger" -- I can't seem to link to it directly). This means that you're actually creating an int32 or an int64 and then converting it to a BigInteger (so it has to be able to fit into the limited size of those types).
To truly take advantage of BigInteger's arbitrary size, you probably want to use BigInteger.Parse(String). This method will return a BigInteger for a numeric String (and it must be a numeric string as defined by the current system culture -- nothing else except a possible leading negative symbol) This method should work perfectly fine in Unity as it's part of the C# standard lib.
So, for your HexToDecimal example, assuming it returns a string you'd use it like
x = BigInteger.Parse(HexToDecimal(hex));
It's just few days ago that I jumped into learning C# and I already have one problem with understanding basics.. Maybe it's just the language barrier (I'm not English native speaker). Please, could you explain me how to understand parsing? For example: while creating a very simple calculator I wanted to read the first input number (which is a variable a). I use this code:
float a = float.Parse(Console.ReadLine());
and the same with b for the other number:
float b = float.Parse(Console.ReadLine());
I learnt that the float is a data type for decimals numbers so what exactly does this particular Parse() stands for?
Obviously, I tried to run the application without parsing and it wouldn't work because it reads it as string, but why? Thank you..
Console.ReadLine() returns a string, which represents a piece of text. So, from the computer's point of view, what you have after calling Console.ReadLine() is a piece of text. It may or may not contain the text "6.0", but from the computer's point of view, it is just a piece of text. As such, you cannot use it to add, subtract etc.
Using the float.Parse(...) method, you tell the computer: "This piece of text actually represents a floating point number, could you please read the text and give me back a number, so that I can start doing math with it?".
The method you are using, float.Parse() is just one of many such methods that take a String input value, and attempt to convert it into the target type, here a float.
There is a safer alternative, however, and it is TryParse():
float a;
if (float.TryParse(Console.ReadLine(), out a))
{
//do something with your new float 'a'
}
In either case, your are asking the framework to inspect the value you provide, and attempt to make a conversion into the requested type. This topic can be quite deep, so you'll want to consult MSDN for the specifics.
Console.ReadLine reads text that the user inputs and returns it to the program so that you may do with it what you want. Therefore, the ReadLine method returns a string.
If you want to work with a decimal (check the decimal class instead of float), you need to convert the string, which is a character sequence, to a number of your desired type, that's where float.Parse comes in:
float.Parse accepts a string and if possible, returns a float value.
Almost every type contains the Parse method which is used to transform a string into the calling one.
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...
I have a brief discussion with my teammate regarding this. He says, if I enter a number in textbox, and try to use the value later on using textbox.text or val(textbox.text), I will not need to parse the value to integer. According to him, if the text attribute value is all number, you can directly get the value as integer, instead of string.
So, if I have textBox1.Text = "12345", then next time, if I use, intABC = textBox1.Text, it will not throw an error. Is it right? Does C# or other .Net language does this implicit conversion? Also, will the code store "12345" as string or integer? And how much memory will this value take, 5bytes for 5 characters or 2bytes for an integer?
TextBox.Text keeps the text as a simple string, it doesn't care about the real "meaning" of the string.
Then, if you want to have your number back, you need to parse the string, hence neither implicit nor explicit cast to int is allowed (or better, it will throw an exception if you do it...).
About the size, that text is stored as an UNICODE (UTF-16) string, hence from 2 to 4 bytes per character (depending on the character).
You can easily measure the size (just the size of the string, without the overhead due to reference size etc.) using the following code:
int numBytes = Encoding.Unicode.GetByteCount(stringToMeasure);
To find more info about strings, unicode and encodings have a look here, here or here .
your friend is wrong, it will make the compiler unhappy, the compiler won't even convert it automatically for you. Text property of a TextBox is of type string. check this
http://msdn.microsoft.com/en-us/library/system.web.ui.webcontrols.textbox.text.aspx
As to your question of other languages; if 'option strict' is not enabled, VB.NET will allow this. It will also allow this assignment if the input is not entirely numeric however, resulting in a runtime exception.
If you know you will only use numerical values, try using a NumericUpDown control.
You could then get/set the numerical value (decimal) by using the Value property.
A NumericUpDown control contains a single numeric value that can be incremented or decremented by clicking the up or down buttons of the control. The user can also enter in a value, unless the ReadOnly property is set to true.