todecimal places long float decimals convert .net decimal precision significant-digits

.net - places - double vs decimal c#



¿Hay alguna manera de obtener las “cifras significativas” de un decimal? (3)

Actualizar

Bien, después de algunas investigaciones, y gracias en gran parte a las respuestas útiles proporcionadas por Jon y Hans, esto es lo que pude juntar. Hasta ahora creo que parece funcionar bien. No apostaría mi vida a su corrección total, por supuesto.

public static int GetSignificantDigitCount(this decimal value) { /* So, the decimal type is basically represented as a fraction of two * integers: a numerator that can be anything, and a denominator that is * some power of 10. * * For example, the following numbers are represented by * the corresponding fractions: * * VALUE NUMERATOR DENOMINATOR * 1 1 1 * 1.0 10 10 * 1.012 1012 1000 * 0.04 4 100 * 12.01 1201 100 * * So basically, if the magnitude is greater than or equal to one, * the number of digits is the number of digits in the numerator. * If it''s less than one, the number of digits is the number of digits * in the denominator. */ int[] bits = decimal.GetBits(value); if (value >= 1M || value <= -1M) { int highPart = bits[2]; int middlePart = bits[1]; int lowPart = bits[0]; decimal num = new decimal(lowPart, middlePart, highPart, false, 0); int exponent = (int)Math.Ceiling(Math.Log10((double)num)); return exponent; } else { int scalePart = bits[3]; // Accoring to MSDN, the exponent is represented by // bits 16-23 (the 2nd word): // http://msdn.microsoft.com/en-us/library/system.decimal.getbits.aspx int exponent = (scalePart & 0x00FF0000) >> 16; return exponent + 1; } }

No lo he probado todo a fondo. Aquí hay algunos ejemplos de entradas / salidas, sin embargo:

Value Precision 0 1 digit(s). 0.000 4 digit(s). 1.23 3 digit(s). 12.324 5 digit(s). 1.2300 5 digit(s). -5 1 digit(s). -5.01 3 digit(s). -0.012 4 digit(s). -0.100 4 digit(s). 0.0 2 digit(s). 10443.31 7 digit(s). -130.340 6 digit(s). -80.8000 6 digit(s).

Usando este código, imagino que lograría mi objetivo haciendo algo como esto:

public static decimal DivideUsingLesserPrecision(decimal x, decimal y) { int xDigitCount = x.GetSignificantDigitCount(); int yDigitCount = y.GetSignificantDigitCount(); int lesserPrecision = System.Math.Min(xDigitCount, yDigitCount); return System.Math.Round(x / y, lesserPrecision); }

Aunque realmente no he terminado de trabajar en esto. Cualquiera que quiera compartir pensamientos: eso sería muy apreciado!

Pregunta original

Supongamos que he escrito este código:

decimal a = 1.23M; decimal b = 1.23000M; Console.WriteLine(a); Console.WriteLine(b);

Lo anterior dará salida:

1.23 1.23000

Encuentro que esto también funciona si uso decimal.Parse("1.23") para a y decimal.Parse("1.23000") para b (lo que significa que esta pregunta se aplica a los casos en que el programa recibe información del usuario).

Así que claramente un valor decimal es de alguna manera "consciente" de lo que llamaré su precisión . Sin embargo, no veo miembros en el tipo decimal que proporcionen ninguna forma de acceder a esto, aparte de ToString .

Supongamos que quisiera multiplicar dos valores decimal y recortar el resultado a la precisión del argumento menos preciso. En otras palabras:

decimal a = 123.4M; decimal b = 5.6789M; decimal x = a / b; Console.WriteLine(x);

Las salidas anteriores:

21.729560302171195125816619416

Lo que pregunto es: ¿cómo podría escribir un método que devolvería 21.73 lugar (ya que 123.4M tiene cuatro cifras significativas)?

Para ser claros: me doy cuenta de que podría llamar a ToString en ambos argumentos, contar las cifras significativas en cada cadena y usar este número para redondear el resultado del cálculo. Estoy buscando una manera diferente , si es posible.

( También me doy cuenta de que en la mayoría de los escenarios donde se trata de cifras significativas, probablemente no necesite usar el tipo decimal . Pero lo pregunto porque, como mencioné al principio, el tipo decimal parece incluir información sobre la precisión, mientras que el double no lo hace, por lo que sé.)


La solución anterior se cae de la cama rápidamente con valores como 1200, donde el número de dígitos significativos es 2 pero la función devuelve 4. Tal vez exista una manera consistente de lidiar con esta situación, es decir, verifique que el valor de retorno no incluya ceros finales en la parte entera del número sin una parte fraccionaria.


Puede usar Decimal.GetBits para obtener los datos en bruto, y resolverlo a partir de eso.

Desafortunadamente, no tengo tiempo para escribir código de muestra en este momento, y probablemente querrá usar BigInteger para algo de la manipulación, si está usando .NET 4, pero espero que esto lo ayude a avanzar. Solo trabajando la precisión y luego llamando a Math.Round en el resultado original puede ser un buen comienzo.


Sí, a diferencia de los tipos de punto flotante, System.Decimal realiza un seguimiento del número de dígitos en el literal. Esta es una característica de decimal.Parse (), ya sea ejecutada por su propio código o por el compilador cuando analiza un literal en su programa. Puede recuperar esta información, verifique el código en mi respuesta en este hilo .

Recuperar el número de dígitos significativos después de hacer matemáticas en el valor me parece una posibilidad muy remota. No tengo idea si los operadores los conservan, por favor háganos saber lo que usted descubre.