L'une des meilleures solutions pour trouver le nombre de chiffres après la virgule est présentée dans le tableau suivant Le message de burning_LEGION .
Ici, j'utilise des parties d'un article du forum STSdb : Nombre de chiffres après le point décimal .
Dans MSDN, nous pouvons lire l'explication suivante :
" Un nombre décimal est une valeur à virgule flottante qui se compose d'un signe, d'une valeur numérique où chaque chiffre de la valeur va de 0 à 9, et un facteur d'échelle qui indique la position d'un point décimal flottant qui sépare les parties intégrales et fractionnaires 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 d'échelle utilisé pour diviser l'entier de 96 bits et spécifier quelle partie est une fraction décimale. et préciser quelle partie de celui-ci est une fraction décimale. Le facteur d'échelle est implicitement le nombre 10, élevé à un exposant allant de 0 à 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 d'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 retourné contient un facteur d'échelle et un signe. Et comme le dit le MSDN, 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 les logiques 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 le point décimal.
Notez qu'ici, MSDN indique également que le facteur d'échelle préserve également les zéros de fin dans un nombre décimal. Les zéros de queue n'affectent pas la valeur d'un nombre décimal dans les opérations arithmétiques ou de comparaison. Cependant, les zéros de queue peuvent être révélés par la méthode ToString si une chaîne de format appropriée est appliquée.
Ces solutions semblent être les meilleures, mais attendez, il y en a d'autres. Par Accès aux méthodes privées en C# nous pouvons utiliser des expressions pour construire 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 (value.flags & ~Int32.MinValue) >> 16
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);
Il s'agit de la méthode la plus rapide pour obtenir le nombre de chiffres après la virgule pour les valeurs décimales.