L'une des meilleures solutions pour trouver le nombre de chiffres après la virgule décimale est indiquée dans le message de burn_LEGION .
Ici, j'utilise des parties d'un article du forum STSdb: Nombre de chiffres après la virgule décimale .
Dans MSDN, nous pouvons lire l'explication suivante:
"Un nombre décimal est une valeur à virgule flottante qui se compose d'un signe, une valeur numérique où chaque chiffre de la valeur est compris entre 0 et 9, et un facteur d'échelle qui indique la position d'une virgule flottante qui sépare l'intégrale et la fraction parties de la valeur numérique. "
Et aussi:
"La représentation binaire d'une valeur décimale se compose d'un signe de 1 bit, d'un nombre entier de 96 bits et d'un facteur de mise à l'échelle utilisé pour diviser l'entier de 96 bits et spécifier quelle partie est une fraction décimale. Le facteur de mise à l'échelle est implicitement le nombre 10, élevé à un exposant compris entre 0 et 28. "
Au niveau interne, la valeur décimale est représentée par quatre valeurs entières.
Il existe une fonction GetBits accessible au public pour obtenir la représentation interne. La fonction renvoie un tableau int []:
[__DynamicallyInvokable]
public static int[] GetBits(decimal d)
{
return new int[] { d.lo, d.mid, d.hi, d.flags };
}
Le quatrième élément du tableau renvoyé contient un facteur d'échelle et un signe. Et comme le MSDN le dit, le facteur d'échelle est implicitement le nombre 10, élevé à un exposant allant de 0 à 28. C'est exactement ce dont nous avons besoin.
Ainsi, sur la base de toutes les investigations ci-dessus, nous pouvons construire notre méthode:
private const int SIGN_MASK = ~Int32.MinValue;
public static int GetDigits4(decimal value)
{
return (Decimal.GetBits(value)[3] & SIGN_MASK) >> 16;
}
Ici, un SIGN_MASK est utilisé pour ignorer le signe. Après logique et nous avons également décalé le résultat de 16 bits vers la droite pour recevoir le facteur d'échelle réel. Cette valeur, enfin, indique le nombre de chiffres après la virgule décimale.
Notez qu'ici MSDN indique également que le facteur de mise à l'échelle préserve également les zéros de fin dans un nombre décimal. Les zéros de fin n'affectent pas la valeur d'un nombre décimal dans les opérations arithmétiques ou de comparaison. Cependant, des zéros de fin peuvent être révélés par la méthode ToString si une chaîne de format appropriée est appliquée.
Cette solution ressemble à la meilleure, mais attendez, il y en a plus. En accédant aux méthodes privées en C #, nous pouvons utiliser des expressions pour créer un accès direct au champ flags et éviter de construire le tableau 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);
}
}
Ce code compilé est affecté au champ GetDigits. Notez que la fonction reçoit la valeur décimale en tant que ref, donc aucune copie réelle n'est effectuée - seulement une référence à la valeur. L'utilisation de la fonction GetDigits de DecimalHelper est simple:
decimal value = 3.14159m;
int digits = DecimalHelper.Instance.GetDigits(ref value);
C'est la méthode la plus rapide possible pour obtenir le nombre de chiffres après la virgule décimale pour les valeurs décimales.