sirve - float c# 2 decimales
C#doble a pérdida de precisión decimal (2)
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:
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
?
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 objetivo es decimal
.
Tenga en cuenta que debido a esto, (decimal)(long)doub3
produce un resultado más preciso que solo (decimal)doub3
.
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 here ) 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.