C# Wrong conversion using Convert.ChangeType() - c#

I am using Convert.ChangeType() to convert from Object (which I get from DataBase) to a generic type T. The code looks like this:
T element = (T)Convert.ChangeType(obj, typeof(T));
return element;
and this works great most of the time, however I have discovered that if I try to cast something as simple as return of the following sql query
select 3.2
the above code (T being double) wont return 3.2, but 3.2000000000000002. I can't realise why this is happening, or how to fix it. Please help!

What you're seeing is an artifact of the way floating-point numbers are represented in memory. There's quite a bit of information available on exactly why this is, but this paper is a good one. This phenomenon is why you can end up with seemingly anomalous behavior. A double or single should never be displayed to the user unformatted, and you should avoid equality comparisons like the plague.
If you need numbers that are accurate to a greater level of precision (ie, representing currency values), then use decimal.

This probably is because of floating point arithmetic. You probably should use decimal instead of double.

It is not a problem of Convert. Internally double type represent as infinite fraction of 2 of real number, that is why you got such result. Depending of your purpose use:
Either Decimal
Or use precise formating {0:F2}
Use Math.Flor/Math.Ceil

Related

Is there a Math API for Pow(decimal, decimal)

Is there a library for decimal calculation, especially the Pow(decimal, decimal) method? I can't find any.
It can be free or commercial, either way, as long as there is one.
Note: I can't do it myself, can't use for loops, can't use Math.Pow, Math.Exp or Math.Log, because they all take doubles, and I can't use doubles. I can't use a serie because it would be as precise as doubles.
One of the multipliyers is a rate : 1/rate^(days/365).
The reason there is no decimal power function is because it would be pointless to use decimal for that calculation. Use double.
Remember, the point of decimal is to ensure that you get exact arithmetic on values that can be exactly represented as short decimal numbers. For reasonable values of rate and days, the values of any of the other subexpressions are clearly not going to be exactly represented as short decimal values. You're going to be dealing with inexact values, so use a type designed for fast calculations of slightly inexact values, like double.
The results when computed in doubles are going to be off by a few billionths of a penny one way or the other. Who cares? You'll round out the error later. Do the rate calculation in doubles. Once you have a result that needs to be turned back into a currency again, multiply the result by ten thousand, round it off to the nearest integer, convert that to a decimal, and then divide it out by ten thousand again, and you'll have a result accurate to four decimal places, which ought to be plenty for a financial calculation.
Here is what I used.
output = (decimal)Math.Pow((double)var1, (double)var2);
Now I'm just learning but this did work but I don't know if I can explain it correctly.
what I believe this does is take the input of var1 and var2 and cast them to doubles to use as the argument for the math.pow method. After that have (decimal) in front of math.pow take the value back to a decimal and place the value in the output variable.
I hope someone can correct me if my explination is wrong but all I know is that it worked for me.
I know this is an old thread but I'm putting this here in case someone finds it when searching for a solution.
If you don't want to mess around with casting and doing you own custom implementation you can install the NuGet DecimalMath.DecimalEx and use it like DecimalEx.Pow(number,power).
Well, here is the Wikipedia page that lists current C# numerics libraries. But TBH I don't think there is a lot of support for decimals
http://en.wikipedia.org/wiki/List_of_numerical_libraries
It's kind of inappropriate to use decimals for this kind of calculation in general. It's high precision yes - but it's also low range. As the MSDN docs state it's for financial/monetary calculations - where there isn't much call for POW unfortunately!
Of course you might have a specific problem domain that needs super high precision and all numbers are within 10(28) - 10(-28). But in that case you will probably just need to write your own series calculator such as the one linked to in the comments to the question.
Not using decimal. Use double instead. According to this thread, the Math.Pow(double, double) is called directly from CLR.
How is Math.Pow() implemented in .NET Framework?
Here is what .NET Framework 4 has (2 lines only)
[SecuritySafeCritical]
public static extern double Pow(double x, double y);
64-bit decimal is not native in this 32-bit CLR yet. Maybe on 64-bit Framework in the future?
wait, huh? why can't you use doubles? you could always cast if you're using ints or something:
int a = 1;
int b = 2;
int result = (int)Math.Pow(a,b);

Am I going crazy or is Math.Pow broken?

I used the base converter from here and changed it to work with ulong values, but when converting large numbers, specifically numbers higher than 16677181699666568 it was returning incorrect values. I started looking into this and discovered that Math.Pow(3, 34) returns the value 16677181699666568, when actually 3^34 is 16677181699666569. This therefore throws a spanner in the works for me. I assume this is just an issue with double precision within the Pow method? Is my easiest fix just to create my own Pow that takes ulong values?
If so, what's the quickest way to do Pow? I assume there's something faster than a for loop with multiplication each time.
You can use BigInteger.Pow. Or use my power method for long.
The problem is that Math.Pow returns a double, and the closest double value to 16677181699666569 is 16677181699666568.
So without getting Math.Pow involved:
long accurate = 16677181699666569;
double closestDouble = accurate;
// See http://pobox.com/~skeet/csharp/DoubleConverter.cs
Console.WriteLine(DoubleConverter.ToExactString(closestDouble));
That prints 16677181699666568.
In other words whatever Math.Pow does internally, it can't return a result that's more accurate than the one you're getting.
As others have said, BigInteger.Pow is your friend if you're using .NET 4.
Read What Every Computer Scientist Should Know About Floating-Point
Floating point types are an approximation, the rounding you see is normal.
If you want exact results use BigInteger.
I assume this is just an issue with
double precision within the Pow
method?
Yes.
Is my easiest fix just to create my
own Pow that takes ulong values?
You can use BigInteger.Pow.
If you're using .NET Framework 4, Microsoft has included a new BigInteger class that lets you manipulate large numbers.
http://msdn.microsoft.com/en-us/library/system.numerics.biginteger.aspx
Alternatively, you can use a nice library that someone else created:
http://intx.codeplex.com/ (IntX library)

c# convert string to single

i have a program running for a while and everything works fine till this weird thing happens. when i convert a number string to Single, i just can't get the value i want. for example:
Convert.ToSingle("11006.954") return the value 11006.9541
Convert.ToSingle("20678.228") return the value 20678.2285
I know i can fix it using Convert.ToDouble but it will take days to modify the existing system. I am using vs2003 .net 1.1.
You seem to be expecting a Single to be able to represent exactly the numbers you've given. It can't. 11006.9541 is the closest Single value to 11006.954. (In fact, the exact value of the closest Single is 11006.9541015625, but I suspect you're seeing 11006.9541 in the debugger.)
If you want accurate representations of numbers originally expressed as decimals, you should use System.Decimal.
See my articles on binary floating point and decimal floating point in .NET for further information.

C# Maths gives wrong results!

I understand the principle behind this problem but it's giving me a headache to think that this is going on throughout my application and I need to find as solution.
double Value = 141.1;
double Discount = 25.0;
double disc = Value * Discount / 100; // disc = 35.275
Value -= disc; // Value = 105.824999999999999
Value = Functions.Round(Value, 2); // Value = 105.82
I'm using doubles to represent quite small numbers. Somehow in the calculation 141.1 - 35.275 the binary representation of the result gives a number which is just 0.0000000000001 out. Unfortunately, since I am then rounding this number, this gives the wrong answer.
I've read about using Decimals instead of Doubles but I can't replace every instance of a Double with a Decimal. Is there some easier way to get around this?
If you're looking for exact representations of values which are naturally decimal, you will need to replace double with decimal everywhere. You're simply using the wrong datatype. If you'd been using short everywhere for integers and then found out that you needed to cope with larger values than that supports, what would you do? It's the same deal.
However, you should really try to understand what's going on to start with... why Value doesn't equal exactly 141.1, for example.
I have two articles on this:
Binary floating point in .NET
Decimal floating point in .NET
You should use decimal – that's what it's for.
The behaviour of floating point arithmetic? That's just what it does. It has limited finite precision. Not all numbers are exactly representable. In fact, there are an infinite number of real valued numbers, and only a finite number can be representable. The key to decimal, for this application, is that it uses a base 10 representation – double uses base 2.
Instead of using Round to round the number, you could use some function you write yourself which uses a small epsilon when rounding to allow for the error. That's the answer you want.
The answer you don't want, but I'm going to give anyway, is that if you want precision, and since you're dealing with money judging by your example you probably do, you should not be using binary floating point maths. Binary floating point is inherently inaccurate and some numbers just can't be represented correctly. Using Decimal, which does base-10 floating point, would be a much better approach everywhere and will avoid you making costly mistakes with your doubles.
After spending most of the morning trying to replace every instance of a 'double' to 'decimal' and realising I was fighting a losing battle, I had another look at my Round function. This may be useful to those who can't implement the proper solution:
public static double Round(double dbl, int decimals) {
return (double)Math.Round((decimal)dbl, decimals, MidpointRounding.AwayFromZero);
}
By first casting the value to a decimal, and then calling Math.Round, this will return the 'correct' value.

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