16

I'm trying to convert an object with the value 0.39999999999999997 to a decimal variable without losing the precision.

object d = 0.39999999999999997;

I've tried the following methods.

decimal val1 = Convert.ToDecimal(d); // val1 = 0.4
object val2 = Convert.ChangeType(d, Type.GetType("System.Decimal")); // val2 = 0.4
decimal val3 = decimal.Parse(d.ToString()); // val3 = 0.4
decimal val4 = (Decimal) d; // val4 = 0.4

I know the this is not a problem with the decimal data type not being able to store this value as illustrated below.

decimal val5 = 0.39999999999999997m; // val5 = 0.39999999999999997;

How do I convert this object to decimal without losing the precision?

I'm using .NET Framework 3.5 if that matters.

Chathura W
  • 351
  • 1
  • 4
  • 14
  • not very sure you can guarantee persistant precision between boxing/unboxing and different floating types conversion. – Tigran Jul 27 '11 at 07:21

8 Answers8

13

I think this is the code you looking for:

object d = 0.39999999999999997;
//Unbox value
double doubleVal = (double)d;

//Convert to string. R format specifier gives a string that can round-trip to an identical number.  
//Without R ToString() result would be doubleAsString = "0.4"
string doubleAsString = doubleVal.ToString("R"); 

//Now that you have doubleAsString = "0.39999999999999997" parse it!
decimal decimalVal = decimal.Parse(doubleAsString);
Renatas M.
  • 11,694
  • 1
  • 43
  • 62
  • Perfect. Although I'm concerned using doubles will affect the data accuracy, this seems like the best option so far. Thanks. – Chathura W Jul 27 '11 at 07:58
  • Well in this case I agree with V4Vendettas comment: `I am afraid then it won't be possible since object holds a reference to the value and in your case unless the reference is of decimal you can't actually get that precision` – Renatas M. Jul 27 '11 at 08:03
6

For this to work you will need to assign it similarly

object d = 0.39999999999999997M;

There is no way for the object to maintain the precision unless you force it to. (If this is not the actual code, you will need to show as how its assigned)

Only then would something like this would work decimal dec = Convert.ToDecimal(d);

V4Vendetta
  • 37,194
  • 9
  • 78
  • 82
  • Because of the way this variable is initialized (the actual value is stored in a database) I have no way of knowing when it'll be a decimal value. – Chathura W Jul 27 '11 at 07:27
  • If you are anyway going to use decimal why not get the database value and convert it to decimal, what is the issue in that ? – V4Vendetta Jul 27 '11 at 07:31
  • Let me clarify. This is in a generic method that accepts objects. And I can't change the method signature. – Chathura W Jul 27 '11 at 07:37
  • 3
    I am afraid then it won't be possible since object holds a reference to the value and in your case unless the reference is of decimal you can't actually get that precision, it would never be able to unbox to a decimal because it was never boxed as one. – V4Vendetta Jul 27 '11 at 07:50
1

As you are reading data from database(as you noted in one of the comments, IMO you should have added that in your question) I think it's a terrible idea to allow converting to double and back while reading from database, because you will lose precision [likely it's stored as fixed point or in a number system that can represent decimals]. I think that you must put some effort to read the stored values directly as decimals(edit your schema or something like that), or if it's not possible, then read them as strings, and use Decimal.Parse() to get actual values.

Actually your number 0.39999999999999997 has 17 decimal places hence it can't be stored as double safely.

P.S. There's a great article regarding .net Doubles and rounding written by Jon Skeet.

Boris Treukhov
  • 17,493
  • 9
  • 70
  • 91
  • I know it's a bad idea to convert to a double and back again. Unfortunately that's the only option I have right now because of the application architecture. Going forward, I think I'm going to go with reading the values as strings and then convert it to Decimal. And thanks for that article. It was a great read. :) – Chathura W Jul 27 '11 at 12:25
0

Seriously all you have to do is something like this...

object d = 0.39999999999999997; 
decimal result;
decimal.TryParse(d.ToString(), out result);
return result;
Sven Schoenung
  • 30,224
  • 8
  • 65
  • 70
Ben Brown
  • 329
  • 2
  • 9
0
string val = "0.39999999999999997");
decimal d = decimal.Parse(val,
    System.Globalization.NumberStyles.AllowDecimalPoint);//0.39999999999999997
Jalal Said
  • 15,906
  • 7
  • 45
  • 68
  • `object d = 0.39999999999999997; double decim = double.Parse(d.ToString(), System.Globalization.NumberStyles.AllowDecimalPoint); //decim = 0.4` – Chathura W Jul 27 '11 at 07:22
  • @Chathura: it is a typo. I mean `decima.Parse` I tested it and the result as expected. – Jalal Said Jul 27 '11 at 07:27
  • Hey where did the string come into picture ? – V4Vendetta Jul 27 '11 at 07:28
  • Whoops. Jumped the gun too quickly it seems. 'd.ToString()' turns it back to 0.4 – Chathura W Jul 27 '11 at 07:30
  • @Chathura: I tested it now and it does not turn back to `0.4`. here is my test: ` string val = "0.39999999999999997"; decimal d = decimal.Parse(val, System.Globalization.NumberStyles.AllowDecimalPoint);/*0.39999999999999997*/ string s = d.ToString();/*0.39999999999999997*/` – Jalal Said Jul 27 '11 at 07:58
  • Yes, but the problem is the initial variable is not a string but an object. When using `object.ToString()` it converts to `0.4` – Chathura W Jul 27 '11 at 08:00
0

Decimal d = new Decimal(d);

if d is a double, then according to the documentation MSDN this should keep the precision.

This constructor rounds value to 15 significant digits using rounding to nearest. This is done even if the number has more than 15 digits and the less significant digits are zero.

Spence
  • 28,526
  • 15
  • 68
  • 103
0

on this page http://msdn.microsoft.com/en-us/library/364x0z75(v=vs.80).aspx it's written "Without the suffix m, the number is treated as a double"
And this code

object o = 0.39999999999999997;
Console.WriteLine(o.GetType());

shows up System.Double

while this one

object o = 0.39999999999999997m;
Console.WriteLine(o.GetType());

shows up System.Decimal
So you're just losing your precision without the suffix m.

pierroz
  • 7,653
  • 9
  • 48
  • 60
  • The Decimal type is necessary because of the way Double stores it's value, it doesn't store the exact value I enter. For instance 12.03 will be stored as 12.029999999999999 etc. – Chathura W Jul 27 '11 at 07:26
0

object d = 0.399999999999999999999999997M; would work you.

Deepansh Gupta
  • 593
  • 4
  • 9