2011-07-27 37 views
10

Estoy tratando de convertir un objeto con el valor 0.39999999999999997 en una variable decimal sin perder la precisión.C# Convertir objeto a Decimal

object d = 0.39999999999999997; 

He intentado los siguientes métodos.

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 

sé que el que esto no es un problema con los datos decimales tipo no ser capaz de almacenar este valor como se ilustra a continuación.

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

¿Cómo convierto este objeto a decimal sin perder la precisión?

Estoy usando .NET Framework 3.5 si eso es importante.

+0

muy seguro de que se puede garantizar la precisión persistente entre el boxeo/unboxing y diferente de conversión de tipos flotantes. – Tigran

Respuesta

8

Creo que este es el código que busca:

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); 
+0

Perfecto. Aunque me preocupa usar dobles afectará la precisión de los datos, esta parece ser la mejor opción hasta ahora. Gracias. –

+0

Bueno, en este caso, estoy de acuerdo con el comentario de V4Vendettas: 'Me temo que no será posible porque el objeto contiene una referencia al valor y en su caso, a menos que la referencia sea decimal, no se puede obtener esa precisión' – Reniuz

+0

Gracias. Gran explicación :) –

0
string val = "0.39999999999999997"); 
decimal d = decimal.Parse(val, 
    System.Globalization.NumberStyles.AllowDecimalPoint);//0.39999999999999997 
+0

'objeto d = 0.39999999999999997; double decim = double.Parse (d.ToString(), System.Globalization.NumberStyles.AllowDecimalPoint); // decim = 0.4' –

+0

@Chathura: es un error tipográfico. Me refiero a 'decima.Parse' Lo probé y el resultado fue el esperado. –

+0

Oye, ¿dónde apareció la cuerda? – V4Vendetta

0

Decimal d = new Decimal(d);

si d es un doble, entonces, de acuerdo a la documentación MSDN Esto debe mantener la precisión.

Este constructor redondea el valor a 15 dígitos significativos mediante el redondeo al más cercano. Esto se hace incluso si el número tiene más de 15 dígitos y los dígitos menos significativos son cero.

+0

No funciona. No se puede pasar un objeto al constructor Decimal(). –

+0

.Net 4.0 y 3.5 ambos tienen un constructor con doble ... – Spence

0

en esta página http://msdn.microsoft.com/en-us/library/364x0z75(v=vs.80).aspx que está escrito "sin el sufijo m, el número se trata como un doble"
Y este código

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

muestra hasta System.Double

mientras éste

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

aparece System.Decimal
Así que solo está perdiendo su precisión sin el sufijo m.

+0

El tipo Decimal es necesario debido a la forma en que Double almacena su valor, no almacena el valor exacto que ingreso. Por ejemplo, 12.03 se almacenará como 12.029999999999999 etc. –

4

Para que esto funcione, necesitará asignar de manera similar

object d = 0.39999999999999997M; 

No hay forma de que el objeto de mantener la precisión a menos que lo fuerza a. (Si este no es el código real, tendrá que demostrar cómo su asignado)

Sólo entonces sería algo como esto funcionaría decimal dec = Convert.ToDecimal(d);

+0

Debido a la forma en que se inicializa esta variable (el valor real se almacena en una base de datos) no tengo forma de saber cuándo será un valor decimal. –

+0

Si de todos modos vas a usar decimal, ¿por qué no obtener el valor de la base de datos y convertirlo a decimal? ¿Cuál es el problema en eso? – V4Vendetta

+0

Déjenme aclarar. Esto es en un método genérico que acepta objetos. Y no puedo cambiar la firma del método. –

0

objeto d = 0.399999999999999999999999997M; te funcionaría.

+0

No es una opción. El valor real se recupera de una base de datos. –

1

Como usted está leyendo datos desde la base de datos (como se anotó en uno de los comentarios, la OMI debería haber añadido que en su pregunta) Creo es una idea terrible permitir que la conversión se duplique y retroceda mientras se lee desde la base de datos, porque perderá precisión [es probable que se almacene como punto fijo o en un sistema numérico que puede representar decimales]. Creo que debe esforzarse un poco para leer los valores almacenados directamente como decimales (edite su esquema o algo así), o si no es posible, léelos como cadenas y use Decimal.Parse() para obtener valores reales .

En realidad, su número 0.39999999999999997 tiene 17 decimales, por lo que no se puede almacenar con el doble de seguridad.

P.S. Hay a great article con respecto a .net Dobles y redondeos escritos por Jon Skeet.

+0

Sé que es una mala idea convertir a un doble y viceversa. Desafortunadamente esa es la única opción que tengo ahora debido a la arquitectura de la aplicación. De ahora en adelante, creo que voy a seguir leyendo los valores como cadenas y luego lo convertiré en Decimal. Y gracias por ese artículo. Fue una gran lectura. :) –

0

serio todo lo que tiene que hacer es algo como esto ...

object d = 0.39999999999999997; 
decimal result; 
decimal.TryParse(d.ToString(), out result); 
return result; 
no
+0

Gracias Sven por la edición –

Cuestiones relacionadas