C # doble a pérdida de precisión decimal

Tengo un doble "138630.78380386264" y quiero convertirlo a un decimal, sin embargo, cuando lo hago, lo hago ya sea mediante conversión o utilizando Convert.ToDecimal() y pierdo precisión.

¿Que esta pasando? Tanto el decimal como el doble pueden contener este número:

introduzca la descripción de la imagen aquí

 double doub = double.Parse("138630.78380386264"); decimal dec = decimal.Parse("138630.78380386264"); string decs = dec.ToString("F17"); string doubse =DoubleConverter.ToExactString(doub); string doubs = doub.ToString("F17"); decimal decC = (decimal) doub; string doudeccs = decC.ToString("F17"); decimal decConv = Convert.ToDecimal(doub); string doudecs = decConv.ToString("F17"); 

Además: ¿cómo puedo hacer que ToString() duplique para imprimir el mismo resultado que muestra el depurador? por ejemplo, 138630.78380386264 ?

138630.78380386264 no es exactamente representable para duplicar la precisión. El número de precisión doble más cercano (tal como se encuentra aquí ) es 138630.783803862635977566242218017578125 , que concuerda con sus hallazgos.

Preguntas por qué la conversión a decimal no contiene más precisión. La documentación para Convert.ToDecimal() tiene la respuesta:

El valor decimal devuelto por este método contiene un máximo de 15 dígitos significativos. Si el parámetro de valor contiene más de 15 dígitos significativos, se redondea utilizando el redondeo al más cercano. El siguiente ejemplo ilustra cómo el método Convert.ToDecimal (doble) utiliza el redondeo al más cercano para devolver un valor decimal con 15 dígitos significativos.

El valor doble, redondeado al más cercano a 15 cifras significativas es 138630.783803863 , exactamente como se muestra arriba.

Es un desafortunado, creo. Cerca de 139,000, un Decimal tiene una precisión mucho mejor que un Double . Pero aún así, debido a este problema, tenemos diferentes Double s que se proyectan en el mismo Decimal . Por ejemplo

 double doub1 = 138630.7838038626; double doub2 = 138630.7838038628; Console.WriteLine(doub1 < doub2); // true, values differ as doubles Console.WriteLine((decimal)doub1 < (decimal)doub2); // false, values projected onto same decimal 

De hecho, hay seis valores Double representables diferentes entre doub1 y doub2 arriba, por lo que no son lo mismo.

Aquí hay un trabajo algo tonto-aronud:

 static decimal PreciseConvert(double doub) { // Handle infinities and NaN-s first (throw exception) // Otherwise: return Decimal.Parse(doub.ToString("R"), NumberStyles.AllowExponent | NumberStyles.AllowDecimalPoint); } 

La cadena de formato "R" asegura que se incluyan suficientes cifras adicionales para hacer el mapeo inyectivo (en el dominio donde Decimal tiene una precisión superior).


Tenga en cuenta que en algunos rangos, un long ( Int64 ) tiene una precisión superior a la del Double . Así que verifiqué si las conversiones aquí se hacen de la misma manera (primer redondeo a 15 decimales significativos). ¡Ellos no son! Asi que:

 double doub3 = 1.386307838038626e18; double doub4 = 1.386307838038628e18; Console.WriteLine(doub3 < doub4); // true, values differ as doubles Console.WriteLine((long)doub3 < (long)doub4); // true, full precision of double used when converting to long 

Parece incoherente utilizar una "regla" diferente cuando el objective es decimal .

Tenga en cuenta que debido a esto, (decimal)(long)doub3 produce un resultado más preciso que solo (decimal)doub3 .