.net Parse() round float values - c#

I found one very interesting thing about parsing a string into a float value. The case is that the string value is obtained from a database by using IDbReader Here is an example:
IDbReader myReader;
...
string sValue = myReader.GetValue(0).ToString(); // 12.339123 for example in VS debug and watch
// the content of sValue is what I want to be at this point, but...
Single fValue = Single.Parse(sValue);
// Now the fValue is 12.34!
If I test just Parse for a constant string, the Parse method works fine:
string sValue = "12.339123";
Single fValue = Single.Parse(sValue);
// Now the fValue is 12.339123!
I am not sure why the content of sValue from IDbReader cannot be parsed as original value (as I can see from VS debug). It rounds the float value to a float value with 2 digits after decimal point. How can I get the original value?
How I can get the exactly same value back?
I am using .Net 2.0
Update: I tried to use GetXXX such as GetFloat or GetDecimal methods to get values directly to a float/decimal value. Unfortunately, all those methods seem having similar rounding issue. Only GetValue() and forcing to string will get the exactly same string value as it is in the database. I thought I could do the conversion to a float or decimal value on .Net side, but the Parse() fails in my case. I am not sure why the rounding happen in Parse(). As some suggested the value are there, and it may be VS debug, watch or log caused rounding. However, when I tried to put the float value to a text box, it is rounded to 2 digits after decimal point. It is really a frustrating issue.

I'm not sure how you're looking at that variable and seeing 12.34. If I do the following in LINQPad:
Single.Parse("12.339123")
I get 12.33912. And if I ask it to give me a string with the full round-trip precision:
Single.Parse("12.339123").ToString("r")
I get 12.3391228, which is as close to exactly 12.339123 as you're going to get with a mere single-precision floating-point value.
Conclusion: you're using an imprecise means to inspect the variable's value. The variable itself is almost certainly fine, and holds just as precise a value as floating-point will allow; it's just that the debugger tooltip, or watch window, or logging library, or whatever it is you're using to look at that value, is only showing you a handful of digits.

try this way:
string sValue = myReader.GetValue(0).ToString("F6");

Related

Convert.ToDouble() throws Format Exception

I am reading Data from an internal Database.
The code assigns value read from the database to double.
if (Convert.ToDouble(PricefromString) == Price && PriceFound == false)
PricefromString has been read from the database.
Most of the time the code works fine, but i get a FormatException, when the string value is an integer value.
In this particular case, when PricefromString = 77, i get the format exception.
I tried debugging and checked the input string to ToDouble() which is throwing the exception.
Edit: PricefromString has a whitespace tab character at the end attached.
It works perfectly fine when the string is a double value eg. 76.99, 77.01, but i get an FormatException error when price reaches an integer value.
Any leads ?
but i get a FormatException, when the string value is an integer value. In this particular case, when PricefromString = 77, i get the format exception.
No, you don't. Convert.ToDouble will work just fine with a string representing an integer, as you can see for yourself by running this code:
Console.WriteLine(Convert.ToDouble("77"));
It does in fact print out 77.
I would urge you to take a long, hard look at what precisely your input string is.
There might be something else, because if 77 is of String data type then definitely it will work.
Check to see if it does not contain any other characters like currency symbols.
Double.Parse or Convert.ToDouble will throw an exception if it cannot parse the given value.
Whereas Double.TryParse returns a bool indicating whether it succeeded.
Try this:
double value;
Double.TryParse(PricefromString, out value);
By this, you can check if it'll work or not and then do the real conversion once value is boolean true.
For more on this, please refer this answer: Parse v. TryParse

C# Convert.ToDouble() loses decimal points when converting string to double

Let's say we have the following simple code
string number = "93389.429999999993";
double numberAsDouble = Convert.ToDouble(number);
Console.WriteLine(numberAsDouble);
after that conversion numberAsDouble variable has the value 93389.43. What can i do to make this variable keep the full number as is without rounding it? I have found that Convert.ToDecimal does not behave the same way but i need to have the value as double.
-------------------small update---------------------
putting a breakpoint in line 2 of the above code shows that the numberAsDouble variable has the rounded value 93389.43 before displayed in the console.
93389.429999999993 cannot be represented exactly as a 64-bit floating point number. A double can only hold 15 or 16 digits, while you have 17 digits. If you need that level of precision use a decimal instead.
(I know you say you need it as a double, but if you could explain why, there may be alternate solutions)
This is expected behavior.
A double can't represent every number exactly. This has nothing to do with the string conversion.
You can check it yourself:
Console.WriteLine(93389.429999999993);
This will print 93389.43.
The following also shows this:
Console.WriteLine(93389.429999999993 == 93389.43);
This prints True.
Keep in mind that there are two conversions going on here. First you're converting the string to a double, and then you're converting that double back into a string to display it.
You also need to consider that a double doesn't have infinite precision; depending on the string, some data may be lost due to the fact that a double doesn't have the capacity to store it.
When converting to a double it's not going to "round" any more than it has to. It will create the double that is closest to the number provided, given the capabilities of a double. When converting that double to a string it's much more likely that some information isn't kept.
See the following (in particular the first part of Michael Borgwardt's answer):
decimal vs double! - Which one should I use and when?
A double will not always keep the precision depending on the number you are trying to convert
If you need to be precise you will need to use decimal
This is a limit on the precision that a double can store. You can see this yourself by trying to convert 3389.429999999993 instead.
The double type has a finite precision of 64 bits, so a rounding error occurs when the real number is stored in the numberAsDouble variable.
A solution that would work for your example is to use the decimal type instead, which has 128 bit precision. However, the same problem arises with a smaller difference.
For arbitrary large numbers, the System.Numerics.BigInteger object from the .NET Framework 4.0 supports arbitrary precision for integers. However you will need a 3rd party library to use arbitrary large real numbers.
You could truncate the decimal places to the amount of digits you need, not exceeding double precision.
For instance, this will truncate to 5 decimal places, getting 93389.42999. Just replace 100000 for the needed value
string number = "93389.429999999993";
decimal numberAsDecimal = Convert.ToDecimal(number);
var numberAsDouble = ((double)((long)(numberAsDecimal * 100000.0m))) / 100000.0;

how the decimal digits is decided in ToString(CultureInfo.NumberFormat)

I met an issue about formatting decimal using CultureInfo in C# today.
I have a decimal fValue has a value of 999, when it runs fValue.ToString(CurrentCulture.NumberFormat) 999.0000 is returned.
While debugging in VS I can get the following values in the Watch window:
fValue //returns 999
fValue.ToString(CurrentCulture.NumberFormat) //the issue returns 999.0000
(999M).ToString(CurrentCulture.NumberFormat) //returns 999
CurrentCulture.NumberFormat.NumberDecimalDigits //returns 2
Why the second value returns 999.0000 rather than 999 or 999.00? Sorry for my crap English someone please help.
Using CurrentCulture.NumberFormat.CurrencyDecimalDigits property you will get the number of decimal digits for Decimal type. 999M type is typeof(Decimal), and it is considered as Currency.
The Decimal struct preserves the precision of the values after parsing from a string, and also as computations are performed. The ToString() method correctly indicates the actual internal precision of the value, regardless of which culture is being used.
However, the debugger appears to display the value in its simplest form instead of showing what ToString() would return, which makes things confusing to say the least.

How to Output a Double Value with all the Decimal Places in Debugger or Console

I wish to extract double value completely when I am debugging an application, so that I can use it to construct a test case to feed into my geometry algorithm.
How to extract the double value out-- down to very last decimal places allowed by the double datatypes in C#-- and output it in either debugger windows, or using Console.WriteLine command?
Edit: My problem is that the algorithm that takes the double value as input will only fail if I insist of input the whole double value, right down to the very last digit. And since I want to reproduce it in a test case, that's why I would need such a full representation of the double value.
I have a DoubleConverter class which does exactly what you want, by the sounds of it. Use it like this:
string text = DoubleConverter.ToExactString(doubleValue);
You need to make sure you understand that just because the output has a large number of digits, that doesn't mean it has that much precision. You may want to read my article on binary floating point in .NET for more information - or you may be aware of all of this to start with, of course.
Note that if you only want a string value which can be round-tripped, you don't need any extra code - just use the "r" format specifier:
string text = doubleValue.ToString("r");
I agree with Jackson Pope's general approach of using tolerance in equality comparisons for tests, but I do find it useful sometimes to see the exact value represented by a double. It can make it easier to understand why a particular calculation has come out one way or another.
Instead of trying to output a binary number as a decimal number to a very large number of decimal places and do an exact comparison, instead do a comparison with an epsilon value that is your acceptable error and set epsilon to be very small. E.g.
double epsilon = Math.Abs(actual - expected);
Assert.That(epsilon, Is.LessThan(0.000000000001);

Convert.ToSingle rounding

I made a query to SQL Server to get some data via a Stored Procedure, the returned value was this:
10219150
Then, in an assembly (I don't have the source code of that assembly, I reflected the file to view the code) someone had written this:
Amount = Convert.ToSingle(10219150); //the value from the stored procedure
So, when I invoke that method which does the final conversion, it returns this value:
1.021315E+7
How is that possible? Why does the Convert.ToSingle add extra decimal positions? I don't understand.
Is there a way that i can reverse that conversion on my code when I invoke that method of the assembly? I can't rewrite that assembly file as it's too big, and, as I mentioned earlier, I don't have the source code to fix the conversion.
From this: 1.021315E+7 To this: 10219150 again (restore the correct value without that conversion)
Hope I made myself clear.
Thanks in advance.
The conversion to single isn't adding extra precision.
10219150 is 1.021315E+7 (which is just another way of writing 1.021315 * 107).
The method you are using to print out the value is just using scientific notation to display the number.
If you are printing the number then you need to set the formatting options.
float amount = Convert.ToSingle("10219150");
string toPrint = string.Format("{0:N}", amount);
Will print the number as:
"10,219,150.00"
To get no decimal places use "{0:N0}" as the format string.
You have two issues. One is easily solved, and the other may be more difficult or impossible.
As ChrisF stated, 1.021315E+7 is simply another way of writing 10219150. (The E+7 part in Scientific Notation means to shift the decimal point 7 places to the right.) When you format your single precision value, you can use
fvalue.ToString("f0");
to display as an integer, rather than in Scientific Notation.
The bigger problem, unfortunately, is that a single precision float can only hold 7 significant digits, and in your example you are storing 8. Therefore, the last digit may be rounded. (Since it happens to be 0 in your case, the rounding might not have been noticed.)
If that loss of precision is critical, you would likely need to fetch the value from the database as a long, or as a double-precision value (depending on the type of data returned.) Each of these types can hold more significant digits.
When the value is converted to Single, it's rounded as it contains more significant digits that can fit in a Single. If you convert 10213153 to Single you also end up with 1.021315E+7 i.e. 10213150.
As the code uses a Single to store the amount, there is nothing that you can do to make it handle the current value correctly. The amount simply can not be represented correctly as a Single.
You either have to use lower values, or change the code.

Categories

Resources