Dies ist eine alte Frage, aber viele Antworten funktionieren bei großen Zahlen nicht gut oder laufen nicht über. Ich denke, die Antwort von D. Nesterov ist die beste: robust, einfach und schnell. Ich möchte nur meine zwei Cent hinzufügen. Ich habe mit Dezimalstellen herumgespielt und auch den Quellcode überprüft . Aus der public Decimal (int lo, int mid, int hi, bool isNegative, byte scale)
Konstruktordokumentation .
Die binäre Darstellung einer Dezimalzahl besteht aus einem 1-Bit-Vorzeichen, einer 96-Bit-Ganzzahl und einem Skalierungsfaktor, mit dem die Ganzzahl geteilt und angegeben wird, welcher Teil davon ein Dezimalbruch ist. Der Skalierungsfaktor ist implizit die Zahl 10, die auf einen Exponenten im Bereich von 0 bis 28 angehoben wird.
In diesem Wissen bestand mein erster Ansatz darin, eine andere zu erstellen, decimal
deren Skalierung den Dezimalstellen entspricht, die ich verwerfen wollte, sie dann abzuschneiden und schließlich eine Dezimalstelle mit der gewünschten Skalierung zu erstellen.
private const int ScaleMask = 0x00FF0000;
public static Decimal Truncate(decimal target, byte decimalPlaces)
{
var bits = Decimal.GetBits(target);
var scale = (byte)((bits[3] & (ScaleMask)) >> 16);
if (scale <= decimalPlaces)
return target;
var temporalDecimal = new Decimal(bits[0], bits[1], bits[2], target < 0, (byte)(scale - decimalPlaces));
temporalDecimal = Math.Truncate(temporalDecimal);
bits = Decimal.GetBits(temporalDecimal);
return new Decimal(bits[0], bits[1], bits[2], target < 0, decimalPlaces);
}
Diese Methode ist nicht schneller als die von D. Nesterov und komplexer, also habe ich ein bisschen mehr herumgespielt. Ich vermute, dass decimal
es langsamer wird, wenn man ein Auxiliar erstellen und die Bits zweimal abrufen muss. Bei meinem zweiten Versuch habe ich die von der Decimal.GetBits (Decimal d) -Methode zurückgegebenen Komponenten selbst manipuliert . Die Idee ist, die Komponenten so oft wie nötig durch 10 zu teilen und den Maßstab zu reduzieren. Der Code basiert (stark) auf der Methode Decimal.InternalRoundFromZero (ref Decimal d, int decimalCount) .
private const Int32 MaxInt32Scale = 9;
private const int ScaleMask = 0x00FF0000;
private const int SignMask = unchecked((int)0x80000000);
// Fast access for 10^n where n is 0-9
private static UInt32[] Powers10 = new UInt32[] {
1,
10,
100,
1000,
10000,
100000,
1000000,
10000000,
100000000,
1000000000
};
public static Decimal Truncate(decimal target, byte decimalPlaces)
{
var bits = Decimal.GetBits(target);
int lo = bits[0];
int mid = bits[1];
int hi = bits[2];
int flags = bits[3];
var scale = (byte)((flags & (ScaleMask)) >> 16);
int scaleDifference = scale - decimalPlaces;
if (scaleDifference <= 0)
return target;
// Divide the value by 10^scaleDifference
UInt32 lastDivisor;
do
{
Int32 diffChunk = (scaleDifference > MaxInt32Scale) ? MaxInt32Scale : scaleDifference;
lastDivisor = Powers10[diffChunk];
InternalDivRemUInt32(ref lo, ref mid, ref hi, lastDivisor);
scaleDifference -= diffChunk;
} while (scaleDifference > 0);
return new Decimal(lo, mid, hi, (flags & SignMask)!=0, decimalPlaces);
}
private static UInt32 InternalDivRemUInt32(ref int lo, ref int mid, ref int hi, UInt32 divisor)
{
UInt32 remainder = 0;
UInt64 n;
if (hi != 0)
{
n = ((UInt32)hi);
hi = (Int32)((UInt32)(n / divisor));
remainder = (UInt32)(n % divisor);
}
if (mid != 0 || remainder != 0)
{
n = ((UInt64)remainder << 32) | (UInt32)mid;
mid = (Int32)((UInt32)(n / divisor));
remainder = (UInt32)(n % divisor);
}
if (lo != 0 || remainder != 0)
{
n = ((UInt64)remainder << 32) | (UInt32)lo;
lo = (Int32)((UInt32)(n / divisor));
remainder = (UInt32)(n % divisor);
}
return remainder;
}
Ich habe keine strengen Leistungstests durchgeführt, aber auf einem MacOS Sierra 10.12.6, 3.06 GHz Intel Core i3-Prozessor und Targeting .NetCore 2.1 scheint diese Methode viel schneller zu sein als die von D. Nesterov (ich werde seitdem keine Zahlen mehr angeben Wie bereits erwähnt, sind meine Tests nicht streng. Es liegt an jedem, der dies implementiert, zu bewerten, ob sich die Leistungssteigerungen für die zusätzliche Codekomplexität auszahlen oder nicht.