Pregunta

Actualizar

OK, después de algunas investigaciones, y gracias en gran parte a las respuestas proporcionadas por votos Jon y Hans, esto es lo que yo era capaz de armar. Hasta ahora creo que parece funcionar bien. No apostaría mi vida en su corrección total de, 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 he probado todo lo que a fondo. Aquí hay algunas entradas / salidas de la muestra, 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).

El uso de este código, me imagino yo lograr mi objetivo de hacer 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);
}

realmente no he terminado de trabajar a través de este, sin embargo. Cualquier persona que quiera compartir pensamientos: eso sería muy apreciado


Original pregunta

Supongamos que tengo escribir este código:

decimal a = 1.23M;
decimal b = 1.23000M;

Console.WriteLine(a);
Console.WriteLine(b);

La salida voluntad por encima de:

1.23
1.23000

Me parece 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 la entrada 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 sobre el tipo decimal que proporcionan alguna forma de acceder a este, aparte de ToString sí mismo.

Supongamos que yo quería multiplicar dos valores decimal y recortar el resultado a la precisión del argumento menos precisa. 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 estoy preguntando es: ¿cómo podría escribir un método que devolvería 21.73 lugar (ya que 123.4M tiene cuatro cifras significativas)

Para que quede claro: me di cuenta de que podría llamar ToString en ambos argumentos, contar las cifras significativas en cada cadena, y utilizar este número para redondear el resultado del cálculo. Estoy en busca de un diferente manera, si es posible.

(I también cuenta de que en la mayoría de los escenarios en los que está tratando con cifras significativas, es probable que no necesita estar usando el tipo decimal. Pero Lo digo porque, como he mencionado en un principio, el tipo decimal aparece para incluir información acerca de la precisión, mientras que double no es así, por lo que yo sé.)

¿Fue útil?

Solución

Se puede usar Decimal.GetBits para obtener el los datos en bruto, y el trabajo fuera de eso.

Por desgracia, no tengo tiempo para escribir código de ejemplo en el momento - y es probable que quieren usar BigInteger para algunos de la manipulación, si está utilizando .NET 4 - pero espero que esto te va. Sólo la elaboración de la precisión y luego llamar Math.Round en el resultado original bien puede ser un buen comienzo.

Otros consejos

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

Me Recuperar el número de dígitos significativos después de hacer cálculos sobre el valor parece una posibilidad muy remota. Ni idea de si los operadores a conservar, por favor háganoslo saber lo que descubres.

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 hay una manera coherente para hacer frente a tal situación es decir, comprobar para asegurarse valor de retorno no incluyen ceros en toda la parte del número sin una parte fraccionaria.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top