Eine der besten Lösungen zum Ermitteln der Anzahl der Stellen nach dem Dezimalpunkt finden Sie im Beitrag von burning_LEGION .
Hier verwende ich Teile aus einem STSdb-Forumsartikel: Anzahl der Nachkommastellen .
In MSDN können wir die folgende Erklärung lesen:
"Eine Dezimalzahl ist ein Gleitkommawert, der aus einem Vorzeichen, einem numerischen Wert, bei dem jede Ziffer im Wert zwischen 0 und 9 liegt, und einem Skalierungsfaktor besteht, der die Position eines Gleitkommapunkts angibt, der das Integral und den Bruch trennt Teile des numerischen Wertes. "
Und auch:
"Die binäre Darstellung eines Dezimalwerts besteht aus einem 1-Bit-Vorzeichen, einer 96-Bit-Ganzzahl und einem Skalierungsfaktor, mit dem die 96-Bit-Ganzzahl geteilt und angegeben wird, welcher Teil davon ein Dezimalbruch ist. Der Skalierungsfaktor ist implizit die Zahl 10, angehoben auf einen Exponenten im Bereich von 0 bis 28. "
Auf interner Ebene wird der Dezimalwert durch vier ganzzahlige Werte dargestellt.
Es gibt eine öffentlich verfügbare GetBits-Funktion zum Abrufen der internen Darstellung. Die Funktion gibt ein int [] Array zurück:
[__DynamicallyInvokable]
public static int[] GetBits(decimal d)
{
return new int[] { d.lo, d.mid, d.hi, d.flags };
}
Das vierte Element des zurückgegebenen Arrays enthält einen Skalierungsfaktor und ein Vorzeichen. Und wie der MSDN sagt, ist der Skalierungsfaktor implizit die Zahl 10, die auf einen Exponenten zwischen 0 und 28 angehoben wird. Genau das brauchen wir.
Basierend auf allen obigen Untersuchungen können wir also unsere Methode konstruieren:
private const int SIGN_MASK = ~Int32.MinValue;
public static int GetDigits4(decimal value)
{
return (Decimal.GetBits(value)[3] & SIGN_MASK) >> 16;
}
Hier wird ein SIGN_MASK verwendet, um das Vorzeichen zu ignorieren. Nach logisch und wir haben auch das Ergebnis mit 16 Bit nach rechts verschoben, um den tatsächlichen Skalierungsfaktor zu erhalten. Dieser Wert gibt schließlich die Anzahl der Stellen nach dem Dezimalpunkt an.
Beachten Sie, dass MSDN hier auch sagt, dass der Skalierungsfaktor auch alle nachgestellten Nullen in einer Dezimalzahl beibehält. Nachgestellte Nullen wirken sich nicht auf den Wert einer Dezimalzahl in Arithmetik- oder Vergleichsoperationen aus. Nachfolgende Nullen werden jedoch möglicherweise von der ToString-Methode angezeigt, wenn eine geeignete Formatzeichenfolge angewendet wird.
Diese Lösung sieht aus wie die beste, aber warten Sie, es gibt noch mehr. Durch den Zugriff auf private Methoden in C # können wir Ausdrücke verwenden, um einen direkten Zugriff auf das Flag-Feld zu erstellen und die Erstellung des int-Arrays zu vermeiden:
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);
}
}
Dieser kompilierte Code wird dem Feld GetDigits zugewiesen. Beachten Sie, dass die Funktion den Dezimalwert als ref empfängt, sodass kein tatsächliches Kopieren durchgeführt wird - nur ein Verweis auf den Wert. Die Verwendung der GetDigits-Funktion von DecimalHelper ist einfach:
decimal value = 3.14159m;
int digits = DecimalHelper.Instance.GetDigits(ref value);
Dies ist die schnellstmögliche Methode, um die Anzahl der Stellen nach dem Dezimalpunkt für Dezimalwerte zu ermitteln.