Una de las mejores soluciones para encontrar el número de dígitos después del punto decimal se muestra en la publicación de burning_LEGION .
Aquí estoy usando partes de un artículo del foro de STSdb: Número de dígitos después del punto decimal .
En MSDN podemos leer la siguiente explicación:
"Un número decimal es un valor de coma flotante que consta de un signo, un valor numérico en el que cada dígito del valor varía de 0 a 9 y un factor de escala que indica la posición de un punto decimal flotante que separa la integral y la fraccional. partes del valor numérico ".
Y también:
"La representación binaria de un valor decimal consta de un signo de 1 bit, un número entero de 96 bits y un factor de escala que se utiliza para dividir el entero de 96 bits y especificar qué parte de él es una fracción decimal. El factor de escala es implícitamente el número 10, elevado a un exponente que va de 0 a 28 ".
A nivel interno, el valor decimal está representado por cuatro valores enteros.
Hay una función GetBits disponible públicamente para obtener la representación interna. La función devuelve una matriz int []:
[__DynamicallyInvokable]
public static int[] GetBits(decimal d)
{
return new int[] { d.lo, d.mid, d.hi, d.flags };
}
El cuarto elemento de la matriz devuelta contiene un factor de escala y un signo. Y como dice el MSDN, el factor de escala es implícitamente el número 10, elevado a un exponente que va de 0 a 28. Esto es exactamente lo que necesitamos.
Por lo tanto, basándonos en todas las investigaciones anteriores, podemos construir nuestro método:
private const int SIGN_MASK = ~Int32.MinValue;
public static int GetDigits4(decimal value)
{
return (Decimal.GetBits(value)[3] & SIGN_MASK) >> 16;
}
Aquí se usa un SIGN_MASK para ignorar el signo. Después de lógica y también hemos desplazado el resultado con 16 bits a la derecha para recibir el factor de escala real. Este valor, finalmente, indica el número de dígitos después del punto decimal.
Tenga en cuenta que aquí MSDN también dice que el factor de escala también conserva los ceros finales en un número decimal. Los ceros finales no afectan el valor de un número decimal en operaciones aritméticas o de comparación. Sin embargo, el método ToString puede revelar ceros finales si se aplica una cadena de formato adecuada.
Esta solución parece la mejor, pero espere, hay más. Al acceder a métodos privados en C # , podemos usar expresiones para crear un acceso directo al campo de banderas y evitar construir la matriz int:
public delegate int GetDigitsDelegate(ref Decimal value);
public class DecimalHelper
{
public static readonly DecimalHelper Instance = new DecimalHelper();
public readonly GetDigitsDelegate GetDigits;
public readonly Expression<GetDigitsDelegate> GetDigitsLambda;
public DecimalHelper()
{
GetDigitsLambda = CreateGetDigitsMethod();
GetDigits = GetDigitsLambda.Compile();
}
private Expression<GetDigitsDelegate> CreateGetDigitsMethod()
{
var value = Expression.Parameter(typeof(Decimal).MakeByRefType(), "value");
var digits = Expression.RightShift(
Expression.And(Expression.Field(value, "flags"), Expression.Constant(~Int32.MinValue, typeof(int))),
Expression.Constant(16, typeof(int)));
return Expression.Lambda<GetDigitsDelegate>(digits, value);
}
}
Este código compilado se asigna al campo GetDigits. Tenga en cuenta que la función recibe el valor decimal como ref, por lo que no se realiza ninguna copia real, solo una referencia al valor. Usar la función GetDigits de DecimalHelper es fácil:
decimal value = 3.14159m;
int digits = DecimalHelper.Instance.GetDigits(ref value);
Este es el método más rápido posible para obtener el número de dígitos después del punto decimal para valores decimales.