Salah satu solusi terbaik untuk menemukan jumlah digit setelah koma desimal ditunjukkan dalam posting burning_LEGION .
Di sini saya menggunakan bagian-bagian dari artikel forum STSdb: Jumlah digit setelah titik desimal .
Di MSDN kita bisa membaca penjelasan berikut:
"Angka desimal adalah nilai floating-point yang terdiri dari tanda, nilai numerik di mana setiap digit dalam nilai berkisar dari 0 hingga 9, dan faktor skala yang menunjukkan posisi titik desimal mengambang yang memisahkan integral dan pecahan bagian dari nilai numerik. "
Dan juga:
"Representasi biner dari nilai Desimal terdiri dari tanda 1-bit, bilangan bulat 96-bit, dan faktor skala yang digunakan untuk membagi bilangan bulat 96-bit dan menentukan bagian mana yang merupakan pecahan desimal. Faktor penskalaannya adalah secara implisit angka 10, dipangkatkan ke eksponen mulai dari 0 hingga 28. "
Pada tingkat internal, nilai desimal diwakili oleh empat nilai integer.
Ada fungsi GetBits yang tersedia untuk umum untuk mendapatkan representasi internal. Fungsi mengembalikan larik int []:
[__DynamicallyInvokable]
public static int[] GetBits(decimal d)
{
return new int[] { d.lo, d.mid, d.hi, d.flags };
}
Elemen keempat dari array yang dikembalikan berisi faktor skala dan tanda. Dan seperti yang dikatakan MSDN, faktor penskalaan secara implisit adalah angka 10, dipangkatkan ke eksponen yang berkisar dari 0 hingga 28. Inilah yang kita butuhkan.
Jadi, berdasarkan semua investigasi di atas, kami dapat membangun metode kami:
private const int SIGN_MASK = ~Int32.MinValue;
public static int GetDigits4(decimal value)
{
return (Decimal.GetBits(value)[3] & SIGN_MASK) >> 16;
}
Di sini SIGN_MASK digunakan untuk mengabaikan tanda. Setelah logis dan kami juga menggeser hasilnya dengan 16 bit ke kanan untuk menerima faktor skala yang sebenarnya. Nilai ini, akhirnya, menunjukkan jumlah digit setelah koma desimal.
Perhatikan bahwa di sini MSDN juga mengatakan faktor penskalaan juga mempertahankan angka nol di belakangnya dalam bilangan Desimal. Nol tertinggal tidak mempengaruhi nilai bilangan desimal dalam operasi aritmatika atau perbandingan. Namun, nol di belakang mungkin akan ditampilkan oleh metode ToString jika string format yang sesuai diterapkan.
Solusi ini sepertinya yang terbaik, tapi tunggu, masih ada lagi. Dengan mengakses metode privat di C # kita bisa menggunakan ekspresi untuk membangun akses langsung ke bidang bendera dan menghindari membangun larik 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);
}
}
Kode yang dikompilasi ini ditugaskan ke bidang GetDigits. Perhatikan bahwa fungsi tersebut menerima nilai desimal sebagai ref, jadi tidak ada penyalinan aktual yang dilakukan - hanya referensi ke nilai tersebut. Menggunakan fungsi GetDigits dari DecimalHelper itu mudah:
decimal value = 3.14159m;
int digits = DecimalHelper.Instance.GetDigits(ref value);
Ini adalah metode tercepat yang mungkin untuk mendapatkan jumlah digit setelah koma desimal untuk nilai desimal.